题目大意:
现有面值为1、2、3、4、5、6的硬币若干枚,现在需要知道能不能将这些硬币分成等额的两堆。
输入:
每行输入6个正整数,分别表是面值为1、2、3、4、5、6的硬币的个数,若输入6个0代表输入结束。单种硬币的数量不会超过20000。
Output若能分割,输出 Can be divided.,若不能输出Can’t be divided.
思路:
要知道能不能分割,只需要求是否存在用若干个硬币,能不能凑成sum/2的情况。也就是dp背包九讲中讲的存在性问题。
只是要知道存在性的话,就不用像多重背包的解法那样复杂.
首先把dp[0][i]初始化为-1,表示无法到达。
dp[0][0] = 0;(表示0可以到达)
然后如果dp[i][j]>=0,表示可以到达 j 这个点,并且还剩下 dp[i] 这么多个的i物品,然后往下一层转移;
if(dp[i-1][j] >= 0)//上一个状态存在
dp[i][j] = num[i];
else
dp[i][j] = -1;
那么我们的第i+1层,就可以在i层此基础上往后递减
for(int j = 0;j <= W-i;j++)
{
if(dp[i][j] > 0)//状态存在且此时dp[i][j] = 个数
dp[i][j+i] = max(dp[i][j+i],dp[i][j] - 1);
}
}
最后搜一下dp[i][v] ( 1 <= i <= n)
如果存在dp[i][v]大于零,说明存在sum/2的情况;
否则不存在。
ac代码:
#include <cstdio>
#include <cstring>
using namespace std;
#define INF -1e8
#define MAX 200
#define WMAX 310005
typedef long long ll;
int n,W;
int t = 0;
ll dp[7][WMAX];
int num[7];//个数
int max(int a,int b)
{
if(a>b)
return a;
else
return b;
}
void solve(void)
{
W = W/2;
for(int i = 1;i<=W;i++)
dp[0][i] = -1;
dp[0][0] = 0;
for(int i = 1;i <= 6;i++)
{
for(int j = 0;j <= W;j++)
{
if(dp[i-1][j] >= 0)//上一个状态存在
dp[i][j] = num[i];
else
dp[i][j] = -1;
}
for(int j = 0;j <= W-i;j++)
{
if(dp[i][j] > 0)//状态存在且此时dp[i][j] = 个数
dp[i][j+i] = max(dp[i][j+i],dp[i][j] - 1);
}
}
if(dp[6][W] == -1)
printf("Collection #%d:\nCan't be divided.\n\n",t);
else
printf("Collection #%d:\nCan be divided.\n\n",t);
}
//E题思路,多重背包问题,先求所有硬币之和,
//奇数肯定不能分等额的两堆,如果偶数,就p看能不能取到硬币和的二分之一
//取到就是可以分
int main(void)
{
while(1)
{
t++;
W = 0;
for(int i = 1;i<=6;i++)
{
scanf("%d",&num[i]);
if(num[i])
W += num[i]*i;
}
if(!W)break;
if(W%2)
printf("Collection #%d:\nCan't be divided.\n\n",t);
else
solve();
}
return 0;
}