洛谷——P1896 [SCOI2005]互不侵犯

P1896 [SCOI2005]互不侵犯

 

状压DP入门题

 

状压DP一般需要与处理状态是否合法,节省时间

设定状态dp[i][j][k]表示第i行第j个状态选择国王数为k的方案数

$dp[i][j][num[j]+p]+=dp[i-1][k][p]$

转移方程如上,表示第i行第j个状态国王数量为$num[j]+p$由上一行第k个状态的p个国王转移而来

 

状压DP的一般套路不就是将数的二进制表示成状态,如1010(10)10这个数就表示第一个位置放,第二个不放,以此类推

预处理:判断这一行的国王是否冲突,i&(i<<1) 想象成二进制,将i左移一位与i比较判断国王是否相邻

如何判断上下两行国王是否冲突呢?

x&y判断上下是否冲突

x&(y<<1) 判断左上右下是否冲突

x&(y>>1) 判断右上左下是否冲突

 

 

#include<bits/stdc++.h>

#define LL long long
using namespace std;

LL MAX,n,m,dp[20][1005][400],can[5000],num[5000],tot,ans;

LL getsum(LL x){
    LL cnt=0;
    while(x) cnt+=(x&1),x>>=1;
    return num[tot]=cnt;
}

int main()
{
    scanf("%lld%lld",&n,&m);
    MAX=(1<<n)-1;
    for(LL i=0;i<=MAX;i++) {
        if(!(i&(i<<1))) can[++tot]=i,dp[1][tot][getsum(i)]=1;
    }
    for(LL i=2;i<=n;i++){
        for(LL j=1;j<=tot;j++){
            LL x=can[j];
            for(LL k=1;k<=tot;k++){
                LL y=can[k];
                if((x&y)||(x&(y<<1))||(x&(y>>1))) continue;
                for(LL p=0;p<=m;p++)
                    dp[i][j][num[j]+p]+=dp[i-1][k][p];
            }
        }
    }
    for(LL i=1;i<=tot;i++) ans+=dp[n][i][m];
    printf("%lld\n",ans);
    return 0;
} 

 

posted @ 2018-09-10 17:35  清风我已逝  阅读(243)  评论(0编辑  收藏  举报