题目地址: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;
}