Dividing (多重背包 动态规划)

Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so that each of them gets the same total value.
Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble of value 1, one of value 3 and two of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the marbles.
Input
Each line in the input describes one collection of marbles to be divided. The lines consist of six non-negative integers n1, n2, …, n6, where ni is the number of marbles of value i. So, the example from above would be described by the input-line “1 0 1 2 0 0”. The maximum total number of marbles will be 20000.

The last line of the input file will be “0 0 0 0 0 0”; do not process this line.
Output
For each colletcion, output “Collection #k:”, where k is the number of the test case, and then either “Can be divided.” or “Can’t be divided.”.

Output a blank line after each test case.
Sample Input
1 0 1 2 0 0
1 0 0 0 1 1
0 0 0 0 0 0
Sample Output
Collection #1:
Can’t be divided.

Collection #2:

Can be divided.



题意:有价值为1 2 3 4 5 6的大理石,对应的数量键盘输入,问两个可不可以分到价值相等的大理石。

思路:此题是多重背包,唯一需要注意的是要将数量二进制转化,将所有物品的数量转化为价值



AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAX 20000+10
using namespace std;
int val[MAX];
int dp[60000+10];
int main()
{
	int num[7];
	int sum;
	int i,j,k;
	int p = 1;
	while(scanf("%d%d%d%d%d%d",&num[1],&num[2],&num[3],&num[4],&num[5],&num[6])!=EOF){
		if(num[1]==0&&num[2]==0&&num[3]==0&&num[4]==0&&num[5]==0&&num[6]==0)
		return 0;
		k=0;  //k现在代表重新分得的物品的数量
		sum=0;
		for(i=1;i<=6;i++){
			sum+=num[i]*i;   //统计总价值
			for(j=1;j<=num[i];j<<=1){  //将数量转化Wie二进制,为什么化为二进制可以做到优化①
				val[k++]=j*i;    //对应价值等于系数乘以价值i
				num[i]-=j;       //数量减去系数
			}
			if(num[i]>0)    //如果不能刚好分完则价值等于剩余价值乘以i
			val[k++]=num[i]*i;
		}
        if(sum&1){    //相当于sum%2==1  总价值为奇数不能平分
     		printf("Collection #%d:\n", p++);
			printf("Can't be divided.\n\n");
			continue;
		}
		memset(dp,0,sizeof(dp));
		sum/=2;             //总价值减半,对其中一个人来说用01背包,背包总容量为sum/2
		for(i=0;i<k;i++){
			for(j=sum;j>=val[i];j--){
				dp[j]=max(dp[j],dp[j-val[i]]+val[i]);
			}
		}
		printf("Collection #%d:\n", p++);
		if(dp[sum]==sum)          //等于背包总容量则另一个人也一样即能平分
            printf("Can be divided.\n\n");
		else
            printf("Can't be divided.\n\n");
	}
	return 0;
}


①(大佬讲解)先解释一下2进制分解,就是给你一个数,比如7,分解一下,就可以得到,1,2,4。1是2的0次幂,2是2的1次幂,4是2的2次幂,也就是把数分解为2的很多次幂。所以如果一个多重背包题目给你了物品的价格,数目,和重量,那么分别记录为p[100],c[100],v[100]。也就是说,我们把物品的个数消去转化为价格和重量,比如c[i]==7,p[i]==1,v[i]==1,那么,我们就分解成1,2,4;进一步说,就是分解成一个p=1,v=1的物品,一个p=2,v=2的物品,一个p=4,v=4的物品,然后这些就可以组成01背包,那么数量这个域就可以消除了,然后用01背包就可以求解多重背包的问题了。

        多重背包还有一种解法就是三层for循环,也就是我们所说的暴力解法,然后就是利用01背包的一维形式来在其中加入背包物品的个数限制,然后进行dp,当然这个方法只是用于小数据的题目,对于大数据会超时。


    原文作者:动态规划
    原文地址: https://blog.csdn.net/J____xueming/article/details/77073381
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞