题意
有一块\(x*y\)的巧克力,问能否恰好分成n块,每块个数如下
输入格式
n
x y
a1 a2 a3 ... an
首先\(x \times y 必然要等于 \sum\limits_{i=1}^{n}a_i\)
设集合状态为S,则转移方程为
\(f(x,y,S)=(f(x,c_0,S_0)\&\& f(x,y-c_0,S_1))\|(f(c_0,y,S_0)\&\& f(x-c_0,y,S_1)) \) 分别对应横着掰和竖着掰
由于 \(x \times y = \sum\limits_{i=1}^{n}a_i\) 故可以简化为f(x,S) x为min(x,y)
1 /* 2 author:jxy 3 lang:C/C++ 4 university:China,Xidian University 5 **If you need to reprint,please indicate the source** 6 */ 7 #include <iostream> 8 #include <cstdio> 9 #include <cstdlib> 10 #include <cstring> 11 #include <algorithm> 12 using namespace std; 13 int org[15]; 14 int sum[1<<15],Max; 15 int f[105][1<<15],vis[105][1<<15]; 16 int count_one(int x) //统计1的个数 17 { 18 x=(x&0x55555555)+(x>>1&0x55555555); 19 x=(x&0x33333333)+(x>>2&0x33333333); 20 x=(x&0x0F0F0F0F)+(x>>4&0x0F0F0F0F); 21 x=(x&0x00FF00FF)+(x>>8&0x00FF00FF); 22 x=(x&0x0000FFFF)+(x>>16&0x0000FFFF); 23 return x; 24 } 25 int dp(int x,int S) 26 { 27 if(vis[x][S])return f[x][S]; //记忆化搜索 28 vis[x][S]=1; 29 int &ans=f[x][S],S1,y=sum[S]/x; 30 ans=0; 31 if(count_one(S)==1)return ans=1; 32 for(int S0=(S-1)&S;S0;S0=(S0-1)&S) 33 { 34 S1=S-S0; 35 if(sum[S0]%x==0&&dp(min(x,sum[S0]/x),S0)&&dp(min(x,sum[S1]/x),S1)) //横着掰 36 return ans=1; 37 if(sum[S0]%y==0&&dp(min(y,sum[S0]/y),S0)&&dp(min(y,sum[S1]/y),S1)) //竖着掰 38 return ans=1; 39 } 40 return ans; 41 } 42 int main() 43 { 44 int n,i; 45 int x,y,C=0; 46 while(~scanf("%d",&n)&&n) 47 { 48 scanf("%d%d",&x,&y); 49 for(i=0;i<n;i++) 50 scanf("%d",&org[i]); 51 memset(vis,0,sizeof(vis)); 52 Max=(1<<n)-1; 53 int S; 54 for(S=1;S<=Max;S++)//记录每一个状态对应的巧克力块和 55 { 56 sum[S]=0; 57 for(i=0;i<n;i++) 58 if(S&(1<<i))sum[S]+=org[i]; 59 } 60 int ans=0; 61 if(sum[Max]==x*y)ans=dp(min(x,y),Max); 62 printf("Case %d: %s\n",++C,ans?"Yes":"No"); 63 } 64 }