Dividing(多重和问题)

来源:互联网 发布:名器大魔王推荐知乎 编辑:程序博客网 时间:2024/06/05 15:33

原题链接
Dividing
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 24102 Accepted Submission(s): 6867

Problem Description

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 eitherCan 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~6的一些收藏品,每种价值的收藏品都有一些或者没有。两个人要判断下这些东西能不能平分。
这就是一个多重和的问题

//下面这种写法虽然是对的,但是会超时,虽然他的思路很简洁,但是由于其复杂度太高所以会超时#include <algorithm>#include <iostream>#include <sstream>#include <cstring>#include <cstdio>#include <string>using namespace std;const int MOD = int(1e9) + 7;//int MOD = 99990001;const int INF = 0x3f3f3f3f;//const LL INFF = 0x3f3f3f3f3f3f3f3fLL;//const DB EPS = 1e-9;//const DB OO = 1e20;//const DB PI = acos(-1.0); //M_PI;const int fx[] = {-1, 1, 0, 0};const int fy[] = {0, 0, -1, 1};const int maxn=100000 + 10;int a[8],m[8];//a表示价值,m表示数量bool dp[8][maxn];int main(){        for(int i=0;i<6;i++)                a[i]=(i+1);//表示价值        int kase=0;        while(scanf("%d%d%d%d%d%d",&m[0],&m[1],&m[2],&m[3],&m[4],&m[5])==6 && m[0]+m[1]+m[2]+m[3]+m[4]+m[5]){                memset(dp,false,sizeof(dp));                int total=0;                for(int i=0;i<6;i++)                        total+=m[i]*a[i];                if(total%2==1){                        printf("Collection #%d:\nCan't be divided.\n\n",++kase);                        continue;                }                total/=2;                dp[0][0]=true;                for(int i=0;i<6;i++){                        for(int j=0;j<=total;j++){                                for(int k=0;k<=m[i]&&k*a[i]<=j;k++){                                        dp[i+1][j] |= dp[i][j-k*a[i]];                                }                        }                }                if(dp[6][total])                        printf("Collection #%d:\nCan be divided.\n\n",++kase);                else                        printf("Collection #%d:\nCan't be divided.\n\n",++kase);        }        return 0;}

如上所示,只需要三重循环也可以简单地把这个问题解决了,但是这明显会超时,所以就要寻找优化的方法,如果用dp[i+1][j]来表示用前i种数加和到j还剩下的数的个数的话,那么状态转移方程就可以是这样的
这里写图片描述
发现前一个i与后一个i是没有关系的,所以dp数组可以优化成1维的

#include <algorithm>#include <iostream>#include <sstream>#include <cstring>#include <cstdio>#include <string>using namespace std;const int MOD = int(1e9) + 7;//int MOD = 99990001;const int INF = 0x3f3f3f3f;//const LL INFF = 0x3f3f3f3f3f3f3f3fLL;//const DB EPS = 1e-9;//const DB OO = 1e20;//const DB PI = acos(-1.0); //M_PI;const int fx[] = {-1, 1, 0, 0};const int fy[] = {0, 0, -1, 1};const int maxn=100000 + 10;//这个数据一定要开大一些int a[8],m[8];//a表示价值,m表示数量int dp[maxn];//dp[j]表示得到j时还剩多少个数字int main(){        for(int i=0;i<6;i++)                a[i]=(i+1);//表示价值        int kase=0;        while(scanf("%d%d%d%d%d%d",&m[0],&m[1],&m[2],&m[3],&m[4],&m[5])==6 && m[0]+m[1]+m[2]+m[3]+m[4]+m[5]){                int total=0;                for(int i=0;i<6;i++)                        total+=m[i]*a[i];                if(total%2==1){                        printf("Collection #%d:\nCan't be divided.\n\n",++kase);                        continue;                }                total/=2;                memset(dp,-1,sizeof(dp));                dp[0]=0;//上面的m和a之所以没有从1开始就是因为怕这里的初始化出问题                for(int i=0;i<6;i++){                        for(int j=0;j<=total;j++){                                if(dp[j]>=0)//本来就剩下了当然就继续剩下了                                        dp[j]=m[i];                                else if(j<a[i]||dp[j-a[i]]<=0)                                        dp[j]=-1;//如果要加的数比总和还大或者是早已经一个数都没剩下了自然这次也不会剩下                                else                                        dp[j]=dp[j-a[i]]-1;//否则就把这个数加进去同时把剩下的数的数目减一然后保存                        }                }                /*                这里之所以能将dp方程降到一维的原因就是原本的dp方程是这样的                dp[i+1][j]=  m[i] || -1 || dp[i+1][j-a[i]]-1      里面没有出现dp[i]与dp[i+1]在同一个方程式的情况,所以可以用一个一维数组最后都可以表示了                */                if(dp[total]>=0)                        printf("Collection #%d:\nCan be divided.\n\n",++kase);                else                        printf("Collection #%d:\nCan't be divided.\n\n",++kase);        }        return 0;}
0 0
原创粉丝点击