题意:给出一个长宽确定的矩形,每次可以沿一条直线把它分割成两块长宽都为整数的矩形,问能否通过多次操作得到n块面积分别为a1,a2...an的矩形。
分析:白书原题,状压DP+记忆化搜索。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n,x,y,num,a[16],sum[1<<15],jud[101][1<<15],val[101][1<<15];
int count(int x)
{
int sum = 0;
while(x)
{
sum++;
x = x & (x-1);
}
return sum;
}
bool Find(int c,int s)
{
if(jud[c][s]) return val[c][s];
if(count(s) == 1) return true;
bool flag = false;
int w = sum[s] / c;
for(int i = (s-1) & s;i;i = (i-1) & s)
if(2*sum[i] >= sum[s])
{
if(sum[i] % c == 0)
{
flag = Find(c,i) && Find(c,s-i);
}
if(flag) break;
if(sum[i] % w == 0)
{
int x = sum[i] / w;
flag = Find(max(x,w),i) && Find(max(c-x,w),s-i);
}
if(flag) break;
}
jud[c][s] = true;
val[c][s] = flag;
return flag;
}
int main()
{
cin.sync_with_stdio(false);
while(cin >> n && n)
{
cout<<"Case "<<++num<<":"<<" ";
memset(sum,0,sizeof(sum));
memset(jud,0,sizeof(jud));
cin>>x>>y;
for(int i = 1;i <= n;i++) cin>>a[i];
int tot = (1<<n)-1;
for(int i = 1;i <= tot;i++)
for(int j = 1;j <= n;j++)
if((i & (1<<(j-1))) == (1<<(j-1))) sum[i] += a[j];
if(sum[tot] != x*y)
{
cout<<"No"<<endl;
continue;
}
if(Find(max(x,y),tot)) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}