Luogu P1896[SCOI2005]互不侵犯

狀態壓縮典型例子?c++

必定要說的一點:數組

\(\color{#fccefa}{不開long long一時爽,提交代碼火葬場}\)spa

SOLUTION:

定義數組\(dp[i][j][s]\),code

表示當前要放第i行的國王blog

算上第i行放上的國王共有j個國王已經被放在棋盤上了get

第i行的狀態是sit

那麼能夠考慮轉移:io

\(dp[i][j][s]=\sum \{dp[i-1][j-cnt(s)][s']|((s'<<1)\&s)||((s'>>1)\&s)||(s' \&s)==0\}\)ast

其中,s'表示上一行的狀態,要保證互不侵犯,則:class

國王周圍的八個格子顯然都不能夠再放國王,那麼顯然從0~(1<<n)的狀態並非均可以取到的(就如\(3_{10}\to 011_{2}\)顯然就是不符合要求的),那麼咱們就不能簡單的從0~(1<<n)枚舉子集(爲何要說這個呢,由於我就直接枚舉了,而後就炸了√)

須要進行dfs來把合法的子集處理出來,這裏用了兩個數組

num[S] 記錄狀態爲S的擺法中,有多少個1

kind[Cnt] 記錄一種符合要求的擺法,其中Cnt是符合要求的擺法的總個數

具體代碼以下:

void dfs(int cnt,int now,int nu,int bj){
    if(cnt==n+1) return;
    if(num[now]==-1)
        kind[++Cnt]=now;
    num[now]=nu;
    if(bj==0) dfs(cnt+1,now+(1<<cnt),nu+1,1);
    dfs(cnt+1,now,nu,0);
}

dfs也就是處理長度爲n的一行每兩個1相隔至少爲1個空位置(0)的擺法

其中:

cnt 表示當前枚舉到了第幾位

now 表示當前狀態下,二進制所對應的十進制是多少

nu 表示目前一共填了幾個1

bj 用於判斷上一位填的是1仍是0

其中0表示上一位填的0 1表示上一位填的1

顯然若是上一位填的是1,那麼這一位咱們只能填0,所以當bj==1時,咱們只dfs這一位填0的狀況,不然dfs兩種狀況。由於會重複搜索到(鬼知道爲何,因此要判斷以前num[now]是否!=-1了(以前是判斷!=0來着,後來發現num[0]就等於0),若是=-1,就須要記錄下來,!=-1,就不須要再記錄一遍了.

初始狀態是\(dp[1][num[kind[i]]][kind[i]]=1\)

也就是第一行的國王,每一種狀態都有一種方法√;

而後就能夠愉快的AC

#include<bits/stdc++.h>

using namespace std;

inline int read() {
    int ans=0;
    char last=' ',ch=getchar();
    while(ch>'9'||ch<'0') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

int n,k,Cnt;
long long dp[10][100][1000];
int num[1000],kind[1000];

void dfs(int cnt,int now,int nu,int bj){
    if(cnt==n+1) return;
    if(num[now]==-1)
        kind[++Cnt]=now;
    num[now]=nu;
    if(bj==0) dfs(cnt+1,now+(1<<cnt),nu+1,1);
    dfs(cnt+1,now,nu,0);
}
bool cmp(int a,int b) {
    return a<b;
}
int main(){
    scanf("%d%d",&n,&k);
    memset(num,-1,sizeof(num));
    dfs(0,0,0,0);
    sort(kind+1,kind+Cnt+1,cmp);
    for(int i=1;i<=Cnt;i++) dp[1][num[kind[i]]][kind[i]]=1;
    for(int i=2;i<=n;i++) {
        for(int a=1;a<=Cnt;a++) {
            for(int b=1;b<=Cnt;b++) {
                if(kind[a]&kind[b]) continue;
                if(kind[a]&(kind[b]<<1)) continue;
                if(kind[a]&(kind[b]>>1)) continue;
                for(int j=k;j>=num[kind[a]];j--) 
                    dp[i][j][kind[a]]+=dp[i-1][j-num[kind[a]]][kind[b]];
            }
        }
    }
    long long ans=0;
    for(int i=1;i<=Cnt;i++) ans+=dp[n][k][kind[i]];
    printf("%lld",ans);
    return 0;
}
相關文章
相關標籤/搜索