LA 4794 状态DP+子集枚举
状态压缩DP,把切割出的面积做状态压缩,统计出某状态下面积和。
设f(x,y,S)为在状态为S下在矩形x,y是否存在可能划分出S包含的面积。若S0是S的子集,对矩形x,y横切中竖切,对竖切若f(x,k,S0)且f(x,y-k,S^S0)为真,则为真,对横切同样。
然后枚举S的子集即可。可以用记忆化搜索。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int dp[105][(1<<15)+5]; bool vis[105][(1<<15)+5]; int area[105]; int se[1<<15]; int counts(int s){ int res=0; while(s){ if(s&1) res++; s>>=1; } return res; } int dfs(int x,int st){ if(vis[x][st]) return dp[x][st]; int y=se[st]/x; vis[x][st]=true; if(counts(st)==1) return dp[x][st]=1; for(int s0=(st-1)&st;s0;s0=(s0-1)&st){ if(se[s0]%x==0&&dfs(min(x,se[s0]/x),s0)&&dfs(min(x,se[s0^st]/x),s0^st)) return dp[x][st]=1; if(se[s0]%y==0&&dfs(min(y,se[s0]/y),s0)&&dfs(min(y,se[s0^st]/y),s0^st)) return dp[x][st]=1; } return dp[x][st]=0; } int main(){ int n,x,y,sum,icase=0; while(scanf("%d",&n),n){ scanf("%d%d",&x,&y); sum=0; for(int i=0;i<n;i++){ scanf("%d",&area[i]); sum+=area[i]; } for(int i=0;i<(1<<n);i++){ se[i]=0; for(int j=0;j<n;j++){ if((1<<j)&i) se[i]+=area[j]; } } memset(vis,false,sizeof(vis)); printf("Case %d: ",++icase); if(sum!=x*y||sum%x!=0||sum%y!=0){ puts("No"); } else{ dfs(min(x,y),(1<<n)-1); if(dp[min(x,y)][(1<<n)-1]) puts("Yes"); else puts("No"); } } return 0; }