5E07 划分大理石 0x5E「动态规划」练习
描述
有价值分别为1..6的大理石各a[1..6]块,现要将它们分成两部分,使得两部分价值之和相等,问是否可以实现。其中大理石的总数不超过20000。
输入格式
有多组数据!
所以可能有多行
如果有0 0 0 0 0 0表示输入文件结束
其余的行为6个整数
输出格式
有多少行可行数据就有几行输出
如果划分成功,输出Can,否则Can't
样例输入
4 7 4 5 9 1
9 8 1 7 2 4
6 6 8 5 9 2
1 6 6 1 0 7
5 9 3 8 8 4
0 0 0 0 0 0
样例输出
Can't
Can
Can't
Can't
Can
题意:
有六种石子,价值是1-6,每种石子有一个数量。现在想把他们平分为价值相等的两堆,问是否可行。
思路:
首先统计一下总的价值,如果价值是奇数,肯定是不行的。
如果价值是偶数,就是一个多重背包问题。
所以先把每种石头用二进制拆分,价值即是价值又是重量。最后看dp[mid]是否等于mid就可以了。
1 //#include <bits/stdc++.h> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<stdio.h> 6 #include<cstring> 7 #include<vector> 8 #include<map> 9 #include<set> 10 11 #define inf 0x3f3f3f3f 12 using namespace std; 13 typedef long long LL; 14 15 int stone[10], dp[300010]; 16 int num[300010], cnt; 17 18 19 void split(int n, int v) 20 { 21 int x = 1, tmp = 0; 22 while(tmp <= n){ 23 num[cnt++] = x * v; 24 x *= 2; 25 tmp += x; 26 } 27 x = n - tmp; 28 if(x){ 29 num[cnt++] = x * v; 30 } 31 } 32 33 34 int main(){ 35 while(true){ 36 int sum = 0; 37 for(int i = 1; i <= 6; i++){ 38 scanf("%d", &stone[i]); 39 sum += stone[i] * i; 40 //dp[i][0] = true; 41 } 42 if(sum == 0){ 43 break; 44 } 45 46 if(sum % 2){ 47 printf("Can't "); 48 continue; 49 } 50 51 cnt = 0; 52 for(int i = 1; i <= 6; i++){ 53 split(stone[i], i); 54 } 55 memset(dp, 0, sizeof(dp)); 56 int mid = sum / 2; 57 for(int i = 0; i < cnt; i++){ 58 for(int j = mid; j >= num[i]; j--){ 59 dp[j] = max(dp[j], dp[j - num[i]] + num[i]); 60 } 61 } 62 63 if(dp[mid] == mid){ 64 printf("Can "); 65 } 66 else{ 67 printf("Can't "); 68 } 69 } 70 return 0; 71 }