背包问题

转载 2016年05月30日 16:49:35

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

相关文章推荐

背包问题详解

  • 2017年04月09日 15:31
  • 269KB
  • 下载

算法与数据结构-背包问题

01背包问题 题目 有N件物品和一个容量为M的背包,每种物品只可以取一件。第i件物品的费用是c[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。 分析 这是最基础的背包问题,特点...

0-1背包问题 全

  • 2015年05月23日 17:00
  • 618KB
  • 下载

0/1背包问题

  • 2015年04月27日 10:06
  • 51KB
  • 下载

01背包问题(回溯算法实现)

问题描述:有n件物品和一个容量为c的背包。第i件物品的价值是v[i],重量是w[i]。求解将哪些物品装入背包可使价值总和最大。所谓01背包,表示每一个物品只有一个,要么装入,要么不装入。     今...

0—1背包问题

  • 2014年11月27日 09:07
  • 3KB
  • 下载

背包问题jasjhiodfjuio

  • 2015年07月07日 18:07
  • 3KB
  • 下载

动态规划的01背包问题---关于国王开矿的故事

此为引用的一篇文章,很棒,地址如下: http://blog.renren.com/share/228709498/2675290555 ----第一节----初识动态规划-------- ...

0-1背包问题

  • 2015年10月17日 22:08
  • 1KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:背包问题
举报原因:
原因补充:

(最多只允许输入30个字)