时间限制: 1Sec 内存限制: 128MB 提交: 555 解决: 238
题目描述
设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于M,而价值的和为最大。
输入
第一行:两个整数,M(背包容量,M≤200)和N(物品数量,N≤30);
第2..N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。
输出
仅一行,一个数,表示最大总价值。
样例输入
10 4 2 1 3 3 4 5 7 9
样例输出
max=12
思路:01背包问题进阶,不同之处在于01背包问题只有选和不选两个种可能,而完全背包问题需要考虑选几个的问题,简单的思路是在01背包问题的基础上,通过增加一重循环控制能放入的数量,01背包因为只需考虑放入和不放入,所以用判断就可以 ,代码如下:
#include <bits/stdc++.h>
using namespace std;
int dp[205];
int w[35],c[35];
int main() {
int n,m;
cin >> m >> n;
for(int i=1;i<=n;i++)
scanf("%d%d",&w[i],&c[i]);
for(int i=1;i<=n;i++) {
for(int j=m;j>=0;j--)
for(int k=0;k<=j/w[i];k++) //此处将判断改为循环,其实已经对当前背包能放下多少当前物品进行控制
dp[j] = max(dp[j],dp[j-k*w[i]]+k*c[i]);
}
cout << "max=" << dp[m];
return 0;
}
这段代码可以AC本题,但是这并不是完全背包问题的最优解,三重循环的设计大大增加了时间复杂度。
回顾01背包问题,本次决策只与上一轮小于等于该当前背包容量的结果有关,用到的是来自上一轮的旧数据,在优化时为了防止数据被覆盖,内层循环遍历时是从背包的最大容量开始的,优化前后的状态转移方程如下动态转移方程如下:
dp[ i ][ j ] = max( dp[ i-1 ][ j ] , dp[ i-1 ][ j-w[i] ]+c[i] )
dp[ j ] = max( dp[ j ] , dp[ j-w[i] ]+c[ j ] )
对于完全背包问题,在考虑时,数据并不会完全来自上轮的旧数据,例如在同一大小的背包下,如果(当前这个物品放两个的价值)高于(放1个当前物品加上上一轮同等规模背包的价值总和),我们显然会两个当前物品,所以,这一轮的数据有可能不来自上一轮,当当前物品的性价比较高的时候,可能会放多个当前物品,而内层循环背包的容量是逐渐增大的,当前物品的个数也是逐渐增大的,所以当前数据可能来自当这一轮更新的数据。打表如图:
箭头就是数据的来源,值得注意的是,当前物品重量为3,背包重量为5时,放入一个重量为2的物品和一个重量为3的物品是最优解,它的来源也是当前新更新的数据。所以可以写出状态转移方程如下:
dp[ i ][ j ] = max( dp[ i-1 ][ j ] , dp[ i ][ j-w[i] ]+c[i]
代码如下:
#include <bits/stdc++.h>
using namespace std;
int dp[205][205];//横坐标表示当前背包体积,纵坐标表示当前物品重量,dp值表示当前总价值
int w[35],c[35];
int main() {
int n,m;
cin >> m >> n;
for(int i=1;i<=n;i++)
scanf("%d%d",&w[i],&c[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(j<w[i]) dp[i][j] = dp[i-1][j];
else dp[i][j] = max(dp[i-1][j],dp[i][j-w[i]]+c[i]);
// for(int i=0;i<=n;i++) {
// for(int j=0;j<=m;j++)
// printf("%d ",dp[i][j]);
// printf("\n");
// }
cout << "max=" << dp[n][m];
return 0;
}
注释部分为打表,和手写的完全相同。
与01背包一样,可以进行同样的优化,状态转移方程为:
dp[ j ] = max( dp[ j ] , dp[ j-w[i] ]+c[ j ] )
代码如下:
#include <bits/stdc++.h>
using namespace std;
int dp[205];
int w[35],c[35];
int main() {
int n,m;
cin >> m >> n;
for(int i=1;i<=n;i++)
scanf("%d%d",&w[i],&c[i]);
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++)
if(j>=w[i])
dp[j] = max(dp[j],dp[j-w[i]]+c[i]);
}
cout << "max=" << dp[m];
return 0;
}
题目来源:题目 2132: 信息学奥赛一本通T1268-完全背包问题