车【状态压缩】

Time Limit:1000MS Memory Limit:65536K
Total Submit:95 Accepted:38


Description
n ∗ n ( n ≤ 20 ) n*n(n≤20) nn(n20)的方格棋盘上放置 n n n个车(可以攻击所在行、列),有些格子不能放,求使它们不能互相攻击的方案总数。


Input
第一行为棋盘的大小 n n n
第二行为障碍的数量 m m m
第三行到第 m + 3 m+3 m+3 m m m个障碍

Output
总数


Sample Input
4
2
1 1
2 2

Sample Output
14


解题思路
在这里插入图片描述
在这里插入图片描述

状态压缩递推(States Compressing Recursion,SCR)

我们一行一行放置。
取棋子的放置情况作为状态,某一列如果已经放置棋子则为 1 1 1,否则为 0 0 0。这样,一个状态就可以用一个最多 20 20 20位的二进制数表示。
例如 n = 5 n=5 n=5,第 1 、 3 、 4 1、3、4 134列已经放置,则这个状态可以表示为 01101 01101 01101(从右到左)。设 f [ s ] f[s] f[s]为达到状态 s s s的方案数,则可以尝试建立 f f f的递推关系。
考虑 n = 5 , s = 01101 n=5,s=01101 n=5,s=01101,
因为我们是一行一行放置的,所以当达到 s s s时已经放到了第三行。又因为一行能且仅能放置一个车,所以我们知道状态 s s s一定来自(没障碍物的情况下):

 1. 前两行在第3、4列放置了棋子(不考虑顺序,下同),第三行在第1列放置;
 2. 前两行在第1、4列放置了棋子,第三行在第3列放置;
 3. 前两行在第1、3列放置了棋子,第三行在第4列放置。

图示:在这里插入图片描述
在枚举s中的1的时候判断一下是否有障碍物即可


代码

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=1<<20; 
int n,m,num,x,y;
long long vis[25],f[maxn];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		vis[x]+=1<<(y-1);
	}
	f[0]=1;
	for(int i=1;i<1<<(n);i++)//枚举可能出现的状态
	{
		int num=0;
		for(int j=i;j;j-=j&(-j)) num++;//计算当前枚举的状态是哪一行
		for(int j=i;j;j-=j&(-j))
			if(!(vis[num]&(j&(-j))))//判断该位置是否可以放置(j&(-j)为这一行你要放的位置,vis[num]为这一行你不能放的位置)
				f[i]+=f[i&~(j&(-j))];
	}
	printf("%lld",f[(1<<n)-1]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值