[状压DP]车

车 车


题目描述

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


输入

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


输出

总数


样例输入

4
2
1 1
2 2

样例输出

14

题目解析

首先,我们看题,想到可以用 D P DP DP来做。以 f [ i ] [ j ] f[i][j] f[i][j]来储存位置 i , j i,j i,j的状态。
但考虑到要考虑到棋盘的状态,所以要用状压DP

状压DP是以DP为基础,将二维压至一维,转化为十进制存储至数组里
然后,我们将 f [ i ] [ j ] f[i][j] f[i][j]压至一维,并且以一个数值 p [ i ] p[i] p[i]表示 2 2 2 i i i次方
通过一个 a a a数组来表示障碍的状态。
通过一位巨佬的指教,我得出了一个转移
a [ x ] = p [ y − 1 ] a[x]=p[y-1] a[x]=p[y1]
然后以一个c来记录当前的行数,得到状态转移方式

for(t=i & ~a[c];t;t-=t & (-t))
		 f[i]+=f[i^(t & (-t))];

(其中 1 1 1是不能放,表放过或有障碍; 0 0 0是可以放)


code

#include<stdio.h>

#include<iostream> 

using namespace std;

long long n,m,x,y,t,c;
long long a[25],p[25],f[1<<20]={1}; 

int main ()
{
	scanf ("%lld%lld",&n,&m);
	p[0]=1;
	for (long long i=1;i<21;++i) p[i]=p[i-1]*2;
	for (long long i=1;i<=m;++i)
	{
		scanf ("%lld%lld",&x,&y);
		a[x]+=p[y-1];
	}
	for (long long i=1;i<p[n];++i)
	{
		for(c=0,t=i;t;t-=t & (-t))c++;
		for(t=i & ~a[c];t;t-=t & (-t))
		 f[i]+=f[i^(t & (-t))];
	}
	printf ("%lld",f[p[n]-1]);
    return 0;
}



posted @ 2020-08-19 10:33  unknown_future  阅读(45)  评论(0编辑  收藏  举报