UVa 1099 分巧克力 ACM解题报告(状压dp)
这题切巧克力,首先看见这题数据很小,n小于等于15,可以直接压缩状态,全集就是(1<<n)-1,然后取他的子集分析
首先要把每个集合内的巧克力的面积和求出来,sum[s]=sigma(a[i]),s&(1<<i)==1;
状态转移方程是dp(s,min(r,sum[s]/r))分成两个子状态dp(s0,min(r,sum[s0]/r))和dp(s1,min(r,sum[s1]/r)),s0+s1=s;sum[s0]+sum[s1]=sum[s]
如果两个子状态都为真,那么总的状态就为真。
dp的边界时bitcount(s)==1,return 1;如果所有分割都不成立,那么return 0
#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<stack>
using namespace std;
#define MAX 50005
typedef long long LL;
const double pi=3.141592653589793;
const int INF=1e9;
const double inf=1e20;
const double eps=1e-6;
int a[16],sum[1<<16],vis[1<<16][105],d[1<<16][105];
int bitcount(int x){
return (x==0)?x:bitcount(x/2)+(x&1);
}
int dp(int s,int x){
if(vis[s][x]) return d[s][x];
vis[s][x]=1;
if(bitcount(s)==1) return d[s][x]=1;
int y=sum[s]/x;
for(int s0=(s-1)&s;s0;s0=(s0-1)&s){
int s1=s-s0;
if(sum[s0]%x==0&&dp(s0,min(x,sum[s0]/x))&&dp(s1,min(x,sum[s1]/x))) return d[s][x]=1;
if(sum[s0]%y==0&&dp(s0,min(y,sum[s0]/y))&&dp(s1,min(y,sum[s1]/y))) return d[s][x]=1;
}
return d[s][x]=0;
}
int main(){
int n,kase=0;
while(cin>>n&&n){
kase++;
int x,y;
scanf("%d%d",&x,&y);
memset(sum,0,sizeof(sum));
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int s=1;s<(1<<n);s++){
for(int i=0;i<n;i++){
if(s&(1<<i)) sum[s]+=a[i];
}
}
int all=(1<<n)-1;
int ans;
if(sum[all]!=x*y) ans=0;
else ans=dp(all,min(x,y));
if(ans) printf("Case %d: Yes\n",kase);
else printf("Case %d: No\n",kase);
}
return 0;
}