SSL1382 车【状压DP】

SSL1382 车【状压DP】_第1张图片
话说状压DP还真是难理解,搞了我一个多小时。
首先,我们发现n,m都非常小,又是求方案总数,所以可得出这是一道状压DP题。
(其实是板子题)
首先我们初始化一个 n u m num numi ,用来表示存 2 i 2^i 2i
我们把每行压缩成一个二进制数,这样障碍直接可表示为:
a [ x ] + = n u m [ y − 1 ] a[x]+=num[y-1] a[x]+=num[y1] ( a [ x ] a[x] a[x] 是存障碍的数组)
这样1表示有障碍,0表示无障碍。

下面进行DP操作
首先上代码:

for(long long i=1; i<num[n]; i++)  //枚举每一个二进制数
     {
     	c=0;
     	for(long long j=i; j; j-=lowbit(j))  //计算行数,有多少个1就有多少行(每行只能放一个1)
     	   c++;
     	for(long long j=i&(~a[c]); j; j-=lowbit(j))  //枚举每一种可转移到当前状态的状态。
     	   f[i]+=f[i^lowbit(j)];
     }

有人可能会有疑问
j=i&(~a[c])是啥?
a [ c ] a[c] a[c] 取反 a n d and and i.
为啥要这样?
因为 a n d and and 的定义,在这里插入图片描述
如果我们1表示有障碍,0表示无障碍,有障碍就不能有贡献,
而 1|1=1,计算出来是有贡献的,互相矛盾,不合法。
取反之后,1|0=0,符合转移。

代码

#include
#include
#include
#include
#include
using namespace std;

long long a[100010],num[100010],f[1<<20]={1};
long long n,m,x,y,c;

long long lowbit(long long x)
{
	return x&(-x);
}
int main()
{
    cin>>n>>m;
    num[0]=1;
    for(long long i=1; i<=n; i++)
       num[i]=num[i-1]*2;
    for(long long i=1; i<=m; i++)
     {
     	scanf("%lld%lld",&x,&y);
     	a[x]+=num[y-1];
     }
    for(long long i=1; i<num[n]; i++)
     {
     	c=0;
     	for(long long j=i; j; j-=lowbit(j))
     	   c++;
     	for(long long j=i&(~a[c]); j; j-=lowbit(j))
     	   f[i]+=f[i^lowbit(j)];
     }
    cout<<f[num[n]-1];
    return 0;
}

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