【BZOJ1087】【codevs2451】互不侵犯,状压DP

传送门1
传送门2
写在前面:第一次写状压DP,感觉还好,至少比数论好些,还有就是让我膜一发位运算……
思路:一看数据范围n<=9而且是省选,就知道这个题九成是状态压缩,题目限制条件与上一行的摆放情况有关,所以这里通过把上一行的国王摆放状态转化为2进制,为0~511,通过511*511的预处理把各二进制数之间的关系求出来,这里是对行与行之间的判断,还要把每一行之中的情况进行预处理,然后就差不多可以DP转移了
f[i][j][p]=sigma(f[i-1][j-num(p)][q]) (num(p)为p的二进制中1的个数,我的代码中是直接打了表的)i为行数,j为已经摆放的国王,p为当前第i行的摆放情况,这里要求p,q均合法且p,q之间不存在攻击状况.
注意:
1.带有位运算的语句括号很重要!真的很重要!
2.多用位运算处理状压DP中的抵触情况,很方便的
代码:

#include"bits/stdc++.h"
#define LL long long
using namespace std;
int n,k,num[513]={0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,5,6,6,7,6,7,7,8,6,7,7,8,7,8,8,9};
bool flag2[512][512],flag1[513];//分别判断行与行之间的关系与每一行之中的关系
LL f[10][90][520],ans;
bool pd(int i,int j)
{
    if(((i&j)==0)&&((i&(j>>1))==0)&&((j&(i>>1))==0)) return 1;//挺神的位运算判断两摆放情况是否抵触,刚开始我是把每个数拆出来了Orz
    else return 0;
}
main()
{
    scanf("%d%d",&n,&k);
    for (int i=0;i<=(1<<n)-1;i++) 
    if ((i&(i>>1))==0) flag1[i]=1;//同样神奇的位运算

    for (int i=0;i<=(1<<n)-1;i++)
    if (flag1[i])
    for (int j=0;j<=(1<<n)-1;j++)
    if (flag1[j])
    flag2[i][j]=pd(i,j);

    f[0][0][0]=1;

    for (int i=1;i<=n;i++)
    for (int j=0;j<=min(k,n*i);j++)
    for (int p=0;p<=(1<<n)-1;p++)
    if (flag1[p]&&j-num[p]>=0)
    for (int q=0;q<=(1<<n)-1;q++)
    if (flag1[q]&&flag2[p][q])
    f[i][j][p]+=f[i-1][j-num[p]][q];
    for (int i=0;i<=(1<<n)-1;i++)
    ans+=f[n][k][i];
    printf("%lld",ans);
}

你可能感兴趣的:(【BZOJ1087】【codevs2451】互不侵犯,状压DP)