1.0-1背包
http://acm.hdu.edu.cn/showproblem.php?pid=2602
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int dp[1002];
int vol[1002];
int val[1002];
int main(){
//freopen("test.txt", "r", stdin);
int T;
cin >> T;
while (T--){
int N, V;
cin >> N >> V;
for (int i = 1; i <= N; i++){
cin >> val[i];
}
for (int i = 1; i <= N; i++){
cin >> vol[i];
}
for (int j = 0; j <= V; j++){
dp[j] = 0;
}
for (int i = 1; i <= N; i++){
for (int j = V; j >= 0; j--){
if (j >= vol[i]){
dp[j] = max(dp[j], dp[j - vol[i]] + val[i]);
}
}
}
cout << dp[V]<<endl;
}
return 0;
}
Description:
话说月光家里有许多玩具,最近他又看上了DK新买的“擎天柱”,就想用自己的跟DK的换。每种玩具都有特定的价格,价格为整数。只有月光拿出的玩具的总价格与“擎天柱”的价格相等才能换得“擎天柱”。同时,月光还希望能用最少的玩具数换回“擎天柱”。请问,月光能顺利得到梦寐以求的“擎天柱”吗?
Input:
输入数据包含多组;对于每组数据,第一行为一个正整数n(1 ≤n≤10); 表示月光手头有n个玩具。接下来一行有n个正整数P1,P2,……,Pn(1 ≤ Pi ≤ 1000),Pi为第i个玩具的所对应的价格。最后一行为一个正整数m(1 ≤ m ≤10000),为“擎天柱”的价格。
Output:
对于每组数据,如果能换得“擎天柱”则输出最少玩具数;否则,输出“-1”。
Sample Input:
3
1 2 3
4
4
4 3 3 5
2
Sample Output:
2
-1
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int dp[10002];
int vol[1002];
//int val[1002];
int main(){
freopen("test.txt", "r", stdin);
int n;
while (cin>>n){
for (int i = 1; i <= n; i++){
cin >> vol[i];
}
int m;
cin >> m;
dp[0] = 0;
for (int j = 1; j <= m; j++){
dp[j] = 1000;//比n大即可
}
for (int i = 1; i <= n; i++){
for (int j = m; j >= 0; j--){
if (j >= vol[i]){//>=
dp[j] = min(dp[j], dp[j - vol[i]] + 1);//min
}
}
}
if (dp[m] == 1000){
cout << -1 << endl;
}
else{
cout << dp[m] << endl;
}
}
return 0;
}
2.背包问题的初始化
想用刚才的方法做?发现题目里多了什么条件没?
对了!那就是“只有月光拿出的玩具的总价格与“擎天柱”的价格相等才能换得“擎天柱”这句。 换句话说题目要求的不仅仅是最优值而且要求你求的是“能把包装满”的最优值!!!
怎么办?难道就这样束手无策了么?
解答:
0-1背包也是背包问题的最常见的两种问法:
一是要求“恰好装满背包”时的最优解
二是“没有要求必须把背包装满”。
这两种问法的实现方法不同点主要在初始化上。
如果是第一种问法,要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞(有时是+),这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。
如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0..V]全部设为0。
为什么呢?
可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。
这个小技巧完全可以推广到其它类型的背包问题,后面也就不再对进行状态转移之前的初始化进行讲解。
3.完全背包
http://acm.hdu.edu.cn/showproblem.php?pid=1248 寒冰王座
转化为01背包问题求解
解题思路:
既然01背包问题是最基本的背包问题,那么我们可以考虑把完全背包问题转化为01背包问题来解。最简单的想法是,考虑到第i种物品最多选V/c[i]件,于是可以把第i种物品转化为V/c[i]件费用及价值均不变的物品,然后求解这个01背包问题。这样完全没有改进基本思路的时间复杂度,但这毕竟给了我们将完全背包问题转化为01背包问题的思路:将一种物品拆成多件物品。
但我们有更优的O(VN)的算法。这个算法使用一维数组,先看伪代码:
for i=1..N
for v=0..V
f[v]=max{f[v],f[v-c[i]]+w[i]};
你会发现,这个伪代码与0-1背包的伪代码只有v的循环次序不同而已。为什么这样一改就可行呢?首先想想为什么0-1背包中要按照v=V..0的逆序来循环。这是因为要保证第i次循环中的状态f[i][v]是由状态f[i-1][v-c[i]]递推而来。换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入第i件物品”这件策略时,依据的是一个绝无已经选入第i件物品的子结果f[i-1][v-c[i]]。而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果f[i][v-c[i]],所以就可以并且必须采用v=0..V的顺序循环。这就是这个简单的程序为何成立的道理。
这个算法也可以以另外的思路得出。例如,基本思路中的状态转移方程可以等价地变形成这种形式:f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]},将这个方程用一维数组实现,便得到了上面的伪代码。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int dp[10002];
//int vol[1002];
//int val[1002];
int main(){
//freopen("test.txt", "r", stdin);
int vol[] = {0, 150, 200, 350 };
int T;
cin >> T;
while (T--){
int N;
cin >> N;
for (int j = 0; j <= N; j++){
dp[j] = 0;
}
for (int i = 1; i <= 3; i++){
for (int j = 0; j <= N;j++){
if (j >= vol[i]){//>=
dp[j] = max(dp[j], dp[j - vol[i]] + vol[i]);
}
}
}
cout << N - dp[N] << endl;
}
return 0;
}
注意这题相当将重量也同时当做价值
4.多重背包
转化为01背包问题
另一种好想好写的基本方法是转化为01背包求解:把第i种物品换成n[i]件01背包中的物品,则得到了物品数为Σn[i]的01背包问题,直接求解,复杂度仍然是O(V*Σn[i])。
http://acm.hdu.edu.cn/showproblem.php?pid=2191
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int dp[102];
int vol[102];
int val[102];
int num[102];
int main(){
//freopen("test.txt", "r", stdin);
int T;
cin >> T;
while (T--){
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++){
cin >> vol[i] >> val[i] >> num[i];
}
for (int i = 0; i <= n; i++){
dp[i] = 0;
}
for (int i = 1; i <= m; i++){
for (int k = 1; k <= num[i]; k++){
for (int j = n; j >= 0; j--){
if (j >= vol[i]){
dp[j] = max(dp[j], dp[j - vol[i]] + val[i]);
}
}
}
}
cout << dp[n] << endl;
}
return 0;
}
还可以
方法是:将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种物品分成系数分别为1、2、4、6的四件物品。
大部分转自http://blog.csdn.net/hackbuteer1/article/details/7178690