【ssl1382】车【状压DP】

Description

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

Input

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

Output

总数

Sample Input

4
2
1 1
2 2

Sample Output

14

分析

状压DP还是比较难理解的知识点,很难检查,做的心态有点崩。

状态的表示是基于2进制的,用位运算去计算枚举和转移。p数组存的是2的n次方,二进制中的类似100…0的数。f数组就是存转移,初始化为1。

我们把每行压缩成一个二进制数,这样障碍直接可表示为:
a [ x ] + = n u m [ y − 1 ] a[x]+=num[y−1] a[x]+=num[y1](a[x] 是存障碍的数组)
这样1表示有障碍,0表示无障碍。

然后枚举 f [ i ] f[i] f[i]之前的所有状态,并转移,也就是可以每一步表示出一个位置放了东西的效果。代码如下:

for(int j=i&(~a[c]);j>0;j-=lowbit(j))
{
    f[i]+=f[i^lowbit(j)];//状态转移方程0
}

上代码

#include
#include
#include
#include
typedef long long ll;
using namespace std;

int n,m;
int p[100001],a[100001],x,y;
ll f[10000001]={1};//不开long long见祖宗!!! 

int lowbit(int k)
{
	return k&(-k);
}

int main()
{
	p[0]=1;
	for(int i=1;i<=20;i++) p[i]=p[i-1]*2;
    cin>>n>>m;
    for(int i=1;i<=m;i++) 
    {
    	cin>>x>>y;
    	a[x]+=p[y-1];
    }
    for(int i=1;i<=p[n]-1;i++)
    {
    	int c=0;
    	for(int j=i;j>0;j-=lowbit(j))
    	{
    		c++;
    	}
    	for(int j=i&(~a[c]);j>0;j-=lowbit(j))
    	{
    		f[i]+=f[i^lowbit(j)];
    	}
    }
    cout<<f[p[n]-1];
	return 0;
}

十年OI一场空,不开longlong见祖宗!!! f f f数组一定要开longlong!

你可能感兴趣的:(DP,#状压DP)