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;
}



你可能感兴趣的:(动态规划,ACM,uva)