UVAlive 4794 Sharing Chocolate
题目:
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=12055
思路:
设d[S][r][c]表示形如r*c的矩形是否可以划分为S中的子集,10表示可否。
转移方程:
d[S][r][c] = d[S0][r0][c] || d[S0][r][c0]
优化:
首先注意到S r c三者知二求一,所以将状态优化为d[S][x]表示有短边x的矩形是否可以分为S的子集,长边==sum(S)/x ,这样恰好迎合了另外一个优化——只计算r*c==S的状态。
代码:
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 5 const int maxn = 15+5; 6 const int maxw = 100 + 5; 7 8 int d[1<<maxn][maxw],vis[1<<maxn][maxw]; 9 int sum[1<<maxn]; 10 int kase=0; 11 12 inline int bitcount(int x) { return x==0?0:bitcount(x/2)+(x&1); } 13 14 int dp(int s,int x) { 15 if(vis[s][x]==kase) return d[s][x]; //记忆化搜索 16 vis[s][x]=kase; 17 if(bitcount(s)==1) return d[s][x]=1; //搜索边界 18 19 int& ans=d[s][x]; 20 int y=sum[s]/x; //根据s与x计算y 21 for(int s0=(s-1)&s;s0;s0=(s0-1)&s) { //枚举子集 //一刀 22 int s1=s-s0; //划分成两个子集 23 if(sum[s0]%x==0 && dp(s0,min(x,sum[s0]/x)) && dp(s1,min(x,sum[s1]/x)) ) return ans=1; //是纵向一刀 24 if(sum[s0]%y==0 && dp(s0,min(y,sum[s0]/y)) && dp(s1,min(y,sum[s1]/y)) ) return ans=1; //抑或横向一刀 25 //如果有一种切法 分成的两个子矩形YES的话那么该矩阵为YES 26 } 27 28 return ans=0; 29 } 30 31 int main() { 32 ios::sync_with_stdio(false); 33 int n,x,y; 34 int A[maxn]; 35 memset(vis,0,sizeof(vis)); 36 37 while(cin>>n && n) { 38 cin>>x>>y; 39 for(int i=0;i<n;i++) cin>>A[i]; 40 41 int full=(1<<n)-1; 42 for(int s=0;s<=full;s++){ //离线计算集合s之和 43 sum[s]=0; 44 for(int j=0;j<n;j++) if(s&(1<<j)) sum[s] += A[j]; 45 } 46 47 int ans; 48 if(sum[full]!=x*y || sum[full]%x!=0) ans=0; //面积相等且形如x*y 49 else 50 ans=dp(full,min(x,y)); 51 52 cout<<"Case "<<++kase<<": "; 53 cout<<(ans? "YES" : "No")<<" "; 54 } 55 return 0; 56 }