状态压缩模板

实现思路:用一个二进制数来表示当前状态。

例题:

        一个n*m的棋盘,要任意摆放若干个棋子,求互不相邻,求方案数,输出结果对1e9+7取余。

思路:

        NO.1     这道题,如果用常规方法做,应该考虑每个点上与上一个点的关系,这样做时间复杂度高,每一部需要考虑的情况多。

        NO.2     先将这道题弱化一下,考虑一个1*n的棋盘。

        我们用1和0来表示两种状态,分别是放和不放,那当前棋盘的状态表示为一个n位的二进制数。即101就表示1*3的棋盘上第一个和第三个放。该棋盘的状态总数就有2^n个,分别是从0000...0(n个0)到1111...1(n个1),遍历过程就由0遍历到1<<n,依次判断该状态是否合法,若合法便将总数加一。 

        对于状态是否合法的判断方法是将该状态进行向左移位,再与该状态进行&运算。若能算出非零结果则说明该状态有相邻情况,跳过;反之,则状态合法将数据更新。

 eg.1  101&1010(合法)                             eg.2  1011&10110(非法)

                  1010                                                          10110

                &  101                                                        & 1011

  ——————————                              ——————————

                   0000                                                           0010

           代码如下:

#include<cstdio>
using namespace std;
int n,ans;
int main(){
	scanf("%d",&n);
	for(int i=0;i<(1<<n);i++){
		if(i&(i<<1))continue;
		ans++;
	}printf("%d\n",ans);
	return 0;
}

               这是一个维的状态递推,现在考虑将1*n转变为n*m,完成思路不过就是将1*m的递推执行n次,但期间要注意每行之间的相邻的放置情况。

          代码如下:

#include<cstdio>
using namespace std;
const int N=1e3+10;
const int MOD=1e9+7;
int n,m,ans;
int dp[N][N];
int main(){
	scanf("%d%d",&n,&m);
	dp[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<(1<<m);j++){
			if(j&(j<<1))continue;//状态不合法直接跳过
			for(int k=0;k<(1<<m);k++){
				//k表示上一行的状态
				if(j&k)continue;//判断上下相邻
				if(k&(k<<1))continue;//状态不合法直接跳过
				dp[i][j]=(dp[i][j]+dp[i-1][k])%MOD;//更新当前状态的总数
			}
		}
	}for(int i=0;i<(1<<m);i++){
		ans=(ans+dp[n][i])%MOD;//统计结果
	}printf("%d\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值