(29)Go动态规划经典思想-01背包问题

方法1:暴力解法 //
每件物品可以放进包,也可以不放进包,时间复杂度O((2^n)*n)

方法2:动态规划
对于每件物品,都有2种选择,放进包和不放进包,分别计算这两种情况哪种包内的价值更大,取最大值
其状态如下图

根据上图状态可知,容积c和i构成数据对,可以定义一个二维数组来存储相应价值,如下图:


func max(v1, v2 int) int {
    if v1 > v2 {
        return v1
    }
    return v2
}

方法1:记忆化搜索
// 时间复杂度O(n*c)
// 空间复杂度O(n*c)
func knapsack2(w, v []int, c int) int {
    n := len(w)
    if n == 0 {
        return 0
    }

    // (n*(c+1))二维数组
    // memo[i][j]: 将[0...i]号物品,填充容量为j的背包的最大价值
    memo := make([][]int, n)
    for i := 0; i < n; i++ {
        memo[i] = make([]int, c+1)
    }
    return bestValue2(w, v, n-1, c, n, memo)
}

// 将[0...index]号物品,填充容积为C的背包的最大价值
func bestValue2(w, v []int, index, c, n int, memo [][]int) int {
    // 递归终止条件
    if index < 0 || c < 0 {
        return 0
    }

    // 如果计算过价值,直接返回该价值
    if memo[index][c] != 0 {
        return memo[index][c]
    }

    // 计算[0...index]物品放进容量c背包的最大价值

    // 2种情况
    // 情况1: index号物品不放进背包
    res := bestValue2(w, v, index-1, c, n)

    // 情况2: 如果背包有足够的空间,index号物品放进背包
    if c >= w[index] {
        res = max(res, v[index]+bestValue1(w, v, index-1, c-w[index], n))
    }

    // 存储结果
    memo[index][c] = res
    return res
}

根据状态方程得知,memo[i]行价值依赖memo[i-1]行结果,因此可以先求0行结果,
再求1行结果,一步步扩大,最终求出[0...n-1]行所有价值
方法2:动态规划
// 时间复杂度O(n*c)
// 空间复杂度O(n*c)
func knapsack3(w, v []int, c int) int {
    n := len(w)
    if n == 0 {
        return 0
    }

    // (n*(c+1))二维数组
    // memo[i][j]: 将[0...i]号物品,填充容量为j的背包的最大价值
    memo := make([][]int, n)
    for i := 0; i < n; i++ {
        memo[i] = make([]int, c+1)
    }

    // 第0行的情况: 将[0...0]物品存进容量为c的背包
    if w[0] <= c {
        for i := w[0]; i <= c; i++ {
            memo[0][i] = v[0]
        }
    }

    // 第[1...length-1]行的情况
    for i := 1; i < n; i++ {
        // [0...i]物品,j空间
        for j := 0; j <= c; j++ {
            // 2种情况
            // 情况1: index号物品不放进背包
            memo[i][j] = memo[i-1][j]

            // 情况2: 如果背包有足够的空间,index号物品放进背包
            if j >= w[i] {
                memo[i][j] = max(memo[i][j], v[i]+memo[i-1][j-w[i]])
            }
        }
    }
    return memo[n-1][c]
}

由方法2可知:第i行元素只依赖于i-1行元素,理论上,只需要保持两行元素即可求出最后一行价值
偶数行在memo[0],奇数行在memo[1]
方法3:动态规划优化1
// 时间复杂度O(n*c)
// 空间复杂度O(2*c)=O(c)
func knapsack4(w, v []int, c int) int {
    n := len(w)
    if n == 0 {
        return 0
    }

    memo := make([][]int, 2)
    for i := 0; i < 2; i++ {
        memo[i] = make([]int, c+1)
    }

    if w[0] <= c {
        for i := w[0]; i <= c; i++ {
            memo[0][i] = v[0]
        }
    }

    for i := 1; i < n; i++ {
        i1 := i % 2       // 本行
        i2 := (i - 1) % 2 // 上一行
        for j := 0; j <= c; j++ {
            memo[i1][j] = memo[i2][j]
            if j >= w[i] {
                memo[i1][j] = max(memo[i1][j], v[i]+memo[i2][j-w[i]])
            }
        }
    }
    return memo[(n-1)%2][c]
}

由方法3可知:i行素只参考i-1行上方和左边的元素,因此可以从右向左刷新 i 行内容
可以只用一行大小为c+1的数组完成动态规划
方法4:动态规划优化2
// 时间复杂度O(n*c)
// 空间复杂度O(c+1)
func knapsack5(w, v []int, c int) int {
    length := len(w)
    if length == 0 {
        return 0
    }

    // memo[c]: 区间[0...index]的物品,填充容积c的背包的最大价值
    // 空间(c+1)
    memo := make([]int, c+1)

    // 第0行的情况: 将[0...0]物品存进容量为c的背包
    if w[0] <= c {
        for i := w[0]; i <= c; i++ {
            memo[i] = v[0]
        }
    }

    for i := 1; i < length; i++ {
        // i个物品,j空间,当j<w[i]时,小于w[i]值均不变,可以提前结束判断
        for j := c; j >= w[i]; j-- {
            memo[j] = max(memo[j], v[i]+memo[j-w[i]])
        }
    }
    return memo[c]
}
测试4种实现方法的性能:
func main() {
    w := []int{1, 2, 3}
    v := []int{6, 10, 12}
    t := time.Now()
    fmt.Println(knapsack2(w, v, 5))
    fmt.Println("记忆化搜索花费时间", t.Sub(time.Now()))
    fmt.Println("========")

    t = time.Now()
    fmt.Println(knapsack3(w, v, 5))
    fmt.Println("动态规划1花费时间", t.Sub(time.Now()))
    fmt.Println("========")

    t = time.Now()
    fmt.Println(knapsack4(w, v, 5))
    fmt.Println("动态规划2花费时间", t.Sub(time.Now()))
    fmt.Println("========")

    t = time.Now()
    fmt.Println(knapsack5(w, v, 5))
    fmt.Println("动态规划2花费时间", t.Sub(time.Now()))
}

// 测试结果
22
记忆化搜索花费时间 -102.325µs
========
22
动态规划1花费时间 -13.581µs
========
22
动态规划2花费时间 -1.596µs
========
22
动态规划2花费时间 -1.346µs

继下一篇《(30)Go动态规划背包思想求解问题》https://www.jianshu.com/p/2f32ad93ee3e

有bug欢迎指出

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容

  • 题目:给定一个n种物品和 一个能装m重量的背包, 物品重量w,价值是p。问:如何才能使背包m重量 ,装最多价值的物...
    梁同桌阅读 567评论 0 0
  • 背包问题(Knapsack problem) 是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品...
    NeoAndBob阅读 3,609评论 0 3
  • 尊敬的陈云贤主席、莫言教授、各位校董、各位嘉宾、老师们、同学们: 每年参加汕大的毕业典礼,我内心总是充满喜悦。在湖...
    溯子的树洞阅读 104评论 0 1
  • 临时接电话妈妈他们今晚回老家了 不需要我明天开车 于是下班直接回家换衣服直奔ktv 跟小弟弟玩儿小游戏等下转...
    桃花花_866d阅读 137评论 0 0
  • 昨夜的雨 氤氲的空气还未散尽 鸟儿在树上啾啾的叫着 树下草坪上的男孩 一个人呆呆地坐着 看着来来往往、 稀稀疏疏的...
    顾摇阅读 138评论 4 5