状态压缩dp例题

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string.h>
#include <queue>
#include <vector>
#define LL long long                       
#define N 10010
#define mod 100000000    //poj3254
using namespace std;    
LL ans, dp[12][1<<12], kind[1<<12];
/*
题意:一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,
在这块牧场放牛,要求两个相邻的方格不能同时放牛,即牛与牛不能相邻。问有多少种放牛方案(一头牛都不放也是一种方案)

思路:状态压缩dp,预处理kind数组内合法的方法(牛同行左右互不相邻),状态转移方程为dp[i][j]+=dp[i-1][t]----其中i-1行的状态t要和i行的状态j不冲突
*/
int a[12];
bool check1(int x)
{
	int flag=0;
	while(x)
	{
		if(x&1)
		{
			if(flag)
				return false;
			else flag=1;
		}
		else flag=0;
		x>>=1;
	}
	return true;
}

bool check2(int x, int k)   //判断实际的放置方案符不符合题目要求
{
	if(kind[x]&&(x|a[k])==a[k])
		return true;
	return false;
}

int main()
{
	int n, m, i, t, j, g, k;
	g=1<<12;
	for(t=0; t<g; ++t)
	{
		if(check1(t))
			kind[t]=1;
	}
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		memset(dp, 0, sizeof(dp));
		memset(a, 0, sizeof(a));
		for(j=0; j<n; ++j)
		{
			for(t=0; t<m; ++t)
			{
				scanf("%d", &g);
				a[j]=a[j]*2+g;
			}
		}
		k=1<<m;
		for(t=0; t<k; ++t)
		{
			if(check2(t, 0))
				dp[0][t]=1;
		}
		for(i=1; i<n; ++i)
		{
			for(j=0; j<k; ++j)
			{
				for(t=0; t<k; ++t)
				{
					if(check2(t, i-1)&&check2(j, i)&&((j&t)==0))
						dp[i][j]+=dp[i-1][t];
				}
				dp[i][j]%=mod;
			}
		}
		for(t=0, ans=0; t<k; ++t)
		{
			if(dp[n-1][t])
				ans+=dp[n-1][t];
			ans%=mod;
		}
		printf("%I64d\n", ans);
	}
	return 0;
}




#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string.h>
#include <queue>
#include <vector>
#define LL long long                       
#define N 10010
using namespace std;    
LL dp[1<<11][11], kind[1<<11];
int k;                           //poj2411
/*
题意:一个矩阵,只能放1*2的木块,问将这个矩阵完全覆盖的不同放法有多少种
思路:dp状态压缩,11表示横放,0表示在该行放竖(下一行对应为1)
*/
bool check1(int x)
{
	int g=0;
	while(x)
	{
		if(x&1)
			g++;
		else 
		{
			if(g&1)
				return false;
			else g=0;
		}
		x>>=1;
	}
	if(g&1)
		return false;
	else return true;
}

bool check2(int j, int t)
{
	if((j|t)!=k-1)
		return false;
	if(kind[j&t])         //这里状态j和状态t不起冲突,它们的不一定kind值都要为1,如1000 |  0111  ,但是1100和0111不行
		return true;
	else return false;
}

int main()
{
	int n, m, i, t, j;
	k=1<<11;
	for(t=0; t<k; ++t)  //枚举各种合法状态
	{
		if(check1(t))
			kind[t]=1;
	}
	while(scanf("%d%d", &n, &m)&&n&&m)
	{
		if(n*m%2)
		{
			printf("0\n");
			continue;
		}
		memset(dp, 0, sizeof(dp));
		k=1<<m;
		for(t=0; t<k; ++t)
		{
			if(kind[t])
				dp[t][0]=1;
		}
		for(i=1; i<n; ++i)
		{
			for(j=0; j<k; ++j)
			{
				for(t=0; t<k; ++t)
				{
					if(!check2(j, t))
						continue;
					dp[j][i]+=dp[t][i-1];
				}
			}
		}
		printf("%I64d\n", dp[k-1][n-1]);  //最后一行肯定都是1
	}
	return 0;
}






#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
using namespace std;   //poj1185   炮兵阵地
/*
总结:这道题和上面的状态压缩不一样,因为如果表示全部状态的话空间不够(其中也包括无用的状态),实际有效的状态只有70以内
      在这里用一个数组s来存储其各个状态
*/
int n, m, T;
int s[70];              //存放炮兵不同的排列状态的数组,最多只有几十种
int sum[70];           //炮兵不同的排列中1的数量
int dp[105][70][70];   //dp[i][j][k]第一维表示行,第二维表示第i行状态为s[j],第三维表示第i-1行状态为s[k]
int map[105];
char a[105][12];
int ok(int x)            //炮兵占有的位为1,没占有的为0
{                           //避免同一行的冲突
	if(x&(x<<1))return 0;
	if(x&(x<<2))return 0;
	return 1;
}
int getsum(int x)           //计算一个状态里1的个数
{
	int s1=0;
	while(x>0)
	{
		if(x&1)
			s1++;
		x=x>>1;
	}
	return s1;
}

void search()  //预处理计算实际可以用的状态总数
{
	int j, k;
	k=1<<m;
	for(j=T=0; j<k; ++j)
	{
		if(ok(j))
		{
			s[T]=j;
			sum[T++]=getsum(j);
		}
	}
	return ;
}

int main()
{
	int i, t, j, k, g, p, s2;
	scanf("%d%d", &n, &m);
	for(t=0; t<n; ++t)
		scanf("%s", &a[t]);
	for(j=0; j<n; ++j)
	{
		for(t=0; t<m; ++t)
		{
			if(a[j][t]=='P')
				map[j]+=(1<<t);
		}
	}
	search();
	for(t=0; t<T;++t)   //与实际情况结合
		if((map[0]|s[t])==map[0])
			dp[0][t][0]=sum[t];
	for(i=1; i<n; ++i)
	{
		for(j=0; j<T; ++j)
		{
			if((map[i]|s[j])!=map[i])continue;
			for(k=0; k<T; ++k)
			{
				if(s[j]&s[k]||(map[i-1]|s[k])!=map[i-1])continue;
				for(t=0; t<T; ++t)
				{
					if(s[t]&s[j]||s[t]&s[k])continue;
					dp[i][j][k]=max(dp[i][j][k], dp[i-1][k][t]+sum[j]);
				}
			}
		}
	}
	for(j=s2=0; j<T; ++j)
	{
		for(t=0; t<T; ++t)
		{
			if(dp[n-1][j][t]>s2)
				s2=dp[n-1][j][t];
		}
	}
	printf("%d\n", s2);
	return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值