每日算法 - 背包问题

2019/03/04

背包问题

1 01背包

一、题目描述

有N件物品和一个容量为V的背包。第i件物品的价格(即体积,下同)是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

二、ruby方案

思路: 主要就是一个问题: 当前物品装还是不装。

求最大,所以动态规划。

dp[i][v] 表示前i个背包装入容量为v的背包中所可以获得的最大价值。(V初始值为最大值)

对于一个物品只有两种情况,装或不装,故状态转移方程

1、第i件不放进去,f[i-1][v]

2、放进去, f[i-1][v-w[i]] + c[i]

1 代码

def bag_01(vals, volumes, bag_volume)
  return 0 if bag_volume <= 0 || vals.nil? || vals.empty? || volumes.nil? || volumes.empty?

  size = vals.size
  dp = Array.new(size){Array.new(bag_volume, 0)}

  dp[0][0] = 0
  i = 1
  while i < size do
    if volumes[i] > bag_volume
      dp[i][bag_volume] = dp[i - 1][bag_volume]
    else
      dp[i][bag_volume] = [dp[i - 1][bag_volume], dp[i - 1][bag_volume - volumes[i]] + vals[i]].max
    end
  end

  dp[size - 1][bag_volume]
end

还有一种思路,一维数组的写法,设 f[v]表示重量不超过v公斤的最大价值, 则f[v]=max(f[v],f[v-w[i]]+c[i]) ,当v>=w[i],1<=i<=n 。

2 典型背包问题

题目描述 Description
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

输入描述 Input Description
输入第一行有两个整数T(1<=T<=1000)和M(1<=M<=100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出描述 Output Description
输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

样例输入 Sample Input
70 3

71 100

69 1

1 2

样例输出 Sample Output
3

数据范围及提示 Data Size & Hint
【数据规模】

对于30%的数据,M<=10;

对于全部的数据,M<=100。

2 完全背包

一、题目描述

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

二、ruby方案

思路:

完全背包和01背包十分相像, 区别就是完全背包物品有无限件。由之前的选或者不选转变成了选或者不选,选几件。

和01背包一样,我们可以写出状态转移方程:f[i][v]=max(f[i-1][v - k x c[i]] + k x w[i]) 0<= k x c[i] <=v

容量为V时,第i件物品选k件时所获取最大价值。

此外可以预处理优化:

当一个物品的价值小于另一个物品的价值,但是价格高于另一个物品,我们就可以不去考虑这个物品。即若两件物品i、j满足c[i]<=c[j]且w[i]>=w[j],则将物品j去掉,不用考虑。

def bag_full(vals, volumes, bag_volume)
  return 0 if bag_volume <= 0 || vals.nil? || vals.empty? || volumes.nil? || volumes.empty?

  size = vals.size
  dp = Array.new(size){Array.new(bag_volume + 1, 0)}

  dp[0][0], dp[1][0], dp[0][1] = 0, 0, 0

  i = 1
  while i < size do
    k = bag_volume / volumes[i]

    dp[i][bag_volume + 1] = dp[i-1][bag_volume]
    1.upto(k).each do |j|
      value = dp[i-1][bag_volume - j * volumes[i]] + k * vals[i]
      dp[i][bag_volume] = value if dp[i][bag_volume] < val
    end
  end

  dp[size][bag_volume]
end

3 多重背包

一 题目描述

有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

2 ruby方案

这里又多了一个限制条件,每个物品规定了可用的次数。

同理,我们可以得出状态转移方程:f[i][v]=max(f[i-1][v-k x w[i]]+ k x c[i]) 0<=k<=n[i]

def bag(vals, volumes, times, bag_volume)
  return 0 if bag_volume <= 0 || vals.nil? || vals.empty? || volumes.nil? || volumes.empty? || times.nil? || times.empty?

  size = vals.size
  dp = Array.new(size){Array.new(bag_volume + 1, 0)}

  dp[0][0], dp[1][0], dp[0][1] = 0, 0, 0

  i = 1
  while i < size do
    k = bag_volume / volumes[i]

    dp[i][bag_volume + 1] = dp[i-1][bag_volume]
    1.upto(k > times[i] ? times[i] : k).each do |j|
      value = dp[i-1][bag_volume - j * volumes[i]] + k * vals[i]
      dp[i][bag_volume] = value if dp[i][bag_volume] < val
    end
  end

  dp[size][bag_volume]
end

题目例子

庆功会

【问题描述】

为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。

【输入格式】

第一行二个数n(n<=500),m(m<=6000),其中n代表希望购买的奖品的种数m表示拨款金额 接下来n行每行3个数vws分别表示第I种奖品的价格价值价格与价值是不同的概念和购买的数量买0件到s件均可),其中v<=100,w<=1000,s<=10。

输出格式

第一行一个数表示此次购买能获得的最大的价值注意不是价格)。

输入样例

5 1000

80 20 4

40 50 9

30 50 7

40 30 6

20 20 1

参考传送门


Show Disqus Comments

Post Directory