状态压缩二 (hiho 1048)

题意:给出一个N*M的矩形格子,用2*1的块去填满块的方案数。

           2<=N<=1000, 3<=m<=5

思路:由于M比较小,可以用位对其进行记录,矩形内1*1的格子的状态有

           横着的开始,横着的结尾,竖着的开始,竖着的结尾,所以用0,1,2,3

           来分别进行表示,这样用一个2bit即可。

           为了加速,先把不可能的一些状态进行剔除,把中间的可能转移状态一次计算好,

           在状态转移的时候直接拿来用就比较省时间。



#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<string>
#include<vector>

using namespace std;

int dp[1111][1111];
int n,nstate,m,q;
int state[1111];
vector<int> v[1111];
const int M=1000000007;

int get(int x,int n)
{
	return (x&(3<<(n<<1)))>>(n<<1);
}

bool isFrist(int x)
{
	for (int i=0;i<m;i++)
	{
		int temp=get(x,i);
		if (temp==0) i++;
		else if (temp==2) continue;
		else return false;
	}
	return true;
}


bool isEnd(int x)
{
	for (int i=0;i<m;i++)
	{
		int temp=get(x,i);
		if (temp==0) i++;
		else if (temp==3) continue;
		else return false;
	}
	return true;
}

bool isok(int x)
{
	for (int i=0;i<m;i++)
	{
		int temp=get(x,i);
		if (temp==0)
		{
			if (temp==m-1) return false;
			if (get(x,i+1)!=1) return false;
			i++;
		}
		else if (temp==1) return false;
	}
	return true;
}

bool isok2(int x,int y)
{
	for (int i=0;i<m;i++)
	{
		//cout<<i<<' '<<get(x,i)<<' '<<get(y,i)<<endl;
		int temp=get(x,i);
		if (temp==2)
		{
			if (get(y,i)!=3) return false;
		}
		else if (get(y,i)==3) return false;
	}
	return true;
}

int main()
{
	freopen("in","r",stdin);
	cin>>n>>m;
	nstate=1<<(m<<1);
	q=0;
	for (int i=0;i<nstate;i++) if (isok(i)) state[q++]=i;

	
	for (int i=0;i<q;i++) if (isFrist(state[i])) dp[0][state[i]]=1;
	for (int i=0;i<q;i++) for (int j=0;j<q;j++) if (isok2(state[i],state[j]))  v[i].push_back(state[j]);
 	
 	for (int i=0;i<n-1;i++) for (int j=0;j<q;j++) if (dp[i][state[j]])
 	{
 	//	cout<<state[j]<<endl;
 		for (int k=0;k<v[j].size();k++)
 			dp[i+1][v[j][k]]=(dp[i+1][v[j][k]]+dp[i][state[j]])%M;
 	}
	
	int ans=0;
	for (int i=0;i<nstate;i++) if (isEnd(i)) ans=(ans+dp[n-1][i])%M;
	cout<<ans<<endl;
	
	//cout<<endl;
	//cout<<isok2(68,68)<<endl;
//	for (int i=0;i<nstate;i++) if (dp[0][i]) cout<<i<<' '<<v[i].size()<<endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值