LA 4794 状态DP+子集枚举

状态压缩DP,把切割出的面积做状态压缩,统计出某状态下面积和。

设f(x,y,S)为在状态为S下在矩形x,y是否存在可能划分出S包含的面积。若S0是S的子集,对矩形x,y横切中竖切,对竖切若f(x,k,S0)且f(x,y-k,S^S0)为真,则为真,对横切同样。

然后枚举S的子集即可。可以用记忆化搜索。

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

int dp[105][(1<<15)+5];
bool vis[105][(1<<15)+5];
int area[105];
int se[1<<15];

int counts(int s){
	int res=0;
	while(s){
		if(s&1) res++;
		s>>=1;
	}
	return res;
}

int dfs(int x,int st){
	if(vis[x][st]) return dp[x][st];
	int y=se[st]/x;
	vis[x][st]=true;
	if(counts(st)==1) return dp[x][st]=1;
	for(int s0=(st-1)&st;s0;s0=(s0-1)&st){
		if(se[s0]%x==0&&dfs(min(x,se[s0]/x),s0)&&dfs(min(x,se[s0^st]/x),s0^st))
		 return dp[x][st]=1;
	 	if(se[s0]%y==0&&dfs(min(y,se[s0]/y),s0)&&dfs(min(y,se[s0^st]/y),s0^st))
	 	 return dp[x][st]=1;
	}
	return dp[x][st]=0;
}

int main(){
	int n,x,y,sum,icase=0;
	while(scanf("%d",&n),n){
		scanf("%d%d",&x,&y);
		sum=0;
		for(int i=0;i<n;i++){
			scanf("%d",&area[i]);
			sum+=area[i];
		}
		for(int i=0;i<(1<<n);i++){
			se[i]=0;
			for(int j=0;j<n;j++){
				if((1<<j)&i) se[i]+=area[j];
			}
		}
		memset(vis,false,sizeof(vis));
		printf("Case %d: ",++icase);
		if(sum!=x*y||sum%x!=0||sum%y!=0){
			puts("No");
		}
		else{
			dfs(min(x,y),(1<<n)-1);
			if(dp[min(x,y)][(1<<n)-1]) puts("Yes");
			else puts("No");
		}
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/jie-dcai/p/4546111.html