您的位置:首页 > 其它

【暑假】[深入动态规划]UVAlive 4794 Sharing Chocolate

2015-08-22 18:22 288 查看
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的状态。

代码:

#include<iostream>
#include<cstring>
using namespace std;

const int maxn = 15+5;
const int maxw = 100 + 5;

int d[1<<maxn][maxw],vis[1<<maxn][maxw];
int sum[1<<maxn];
int kase=0;

inline int bitcount(int x) { return x==0?0:bitcount(x/2)+(x&1); }

int dp(int s,int x) {
if(vis[s][x]==kase) return d[s][x];  //记忆化搜索
vis[s][x]=kase;
if(bitcount(s)==1) return d[s][x]=1;  //搜索边界

int& ans=d[s][x];
int y=sum[s]/x;                     //根据s与x计算y
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 ans=1;  //是纵向一刀
if(sum[s0]%y==0 && dp(s0,min(y,sum[s0]/y)) && dp(s1,min(y,sum[s1]/y)) ) return ans=1;  //抑或横向一刀
//如果有一种切法 分成的两个子矩形YES的话那么该矩阵为YES
}

return ans=0;
}

int main() {
ios::sync_with_stdio(false);
int n,x,y;
int A[maxn];
memset(vis,0,sizeof(vis));

while(cin>>n && n) {
cin>>x>>y;
for(int i=0;i<n;i++) cin>>A[i];

int full=(1<<n)-1;
for(int s=0;s<=full;s++){  //离线计算集合s之和
sum[s]=0;
for(int j=0;j<n;j++) if(s&(1<<j)) sum[s] += A[j];
}

int ans;
if(sum[full]!=x*y || sum[full]%x!=0) ans=0;  //面积相等且形如x*y
else
ans=dp(full,min(x,y));

cout<<"Case "<<++kase<<": ";
cout<<(ans? "YES" : "No")<<"\n";
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: