UVA1099----Sharing Chocolate----在集合上的DP
题目地址:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3540
题目意思:
给你一块X*Y的巧克力
问你是否可以分成N块大小分别为AI的小巧克力
解题思路:
我们用F[X][S]表示能否分成将一个小边为X且集合为S
切的时候分两种,横切和竖切
横切则是X不变,竖切则是Y不变,可以切成两个子集,按记忆化搜索
对于那些x*y不等于sum[s]的我们可以直接不计算,因为无法满足,没有计算的必要
下面上代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 16; const int maxs = 1<<maxn; const int maxx = 110; int a[maxn]; int f[maxx][maxs]; bool vis[maxx][maxs]; int sum[maxs]; int x,y,n; int all; int bitcount(int x) { return x==0?0:bitcount(x>>1)+(x&1); } int dp(int S,int x) { if(vis[x][S]) return f[x][S]; vis[x][S]=1; int &ans = f[x][S]; if(bitcount(S)==1) return ans=1; int y = sum[S]/x; //枚举子集S0 for(int S0=(S-1)&S;S0;S0=(S0-1)&S) { int S2 = S-S0; if(sum[S0]%x==0 && dp(S0,min(x,sum[S0]/x)) && dp(S2,min(x,sum[S2]/x))) return ans = 1; if(sum[S0]%y==0 && dp(S0,min(y,sum[S0]/y)) && dp(S2,min(y,sum[S2]/y))) return ans = 1; } return ans = 0; } int main() { int ca = 1; while(~scanf("%d",&n) && n) { memset(sum,0,sizeof(sum)); scanf("%d%d",&x,&y); for(int i=0;i<n;i++) scanf("%d",&a[i]); for(int s=0;s<(1<<n);s++) { for(int i=0;i<n;i++) { if(s&(1<<i)) sum[s]+=a[i]; } } all = (1<<n)-1; memset(vis,false,sizeof(vis)); int ans; if(sum[all] != x*y || sum[all]%x!=0) ans = 0; else ans = dp(all,min(x,y)); printf("Case %d: %s\n",ca++,ans?"Yes":"No"); } return 0; }