DP - 狀壓DP - SGU - 223 - Little Kings

DP - 狀壓DP - SGU - 223 - Little Kings

在 n×n 的棋盤上放 k 個國王,國王可攻擊相鄰的 8 個格子,求使它們無法互相攻擊的方案總數。

輸入格式
共一行,包含兩個整數 n 和 k。

輸出格式
共一行,表示方案總數,若不能夠放置則輸出0。

數據範圍
1≤n≤10,
0≤k≤n2

輸入樣例:
3 2
輸出樣例:
16

分析:

本題與——《蒙德里安的夢想》相似,只是擺放的物品佔據的範圍不同。

經過分析,容易發現:由於每個國王僅佔據相鄰的八個方格,故每一行擺放的方案僅與上一行的狀態有關。

si10那麼我們可以通過一個二進制數s來表示第i行的狀態,1表示該位置放一個國王,0表示不放。\\由於國王的數量有限制,因此我們的狀態表示需要三維。

f[i][j][s]ijis狀態表示,f[i][j][s]:已經擺放了前i行,且用了j個物品,且第i行的狀態是s的總方案數。

iai1bab滿i1b1狀態計算:\\假設第i行的狀態是a,第i-1行的狀態是b,若a能夠由b轉移而來,需要滿足:\\①、第i-1行本身是合法的:二進制數b不能存在連續的1。

abi滿a&b=0②、狀態a和狀態b不能存在衝突:\\\qquadⅠ、第i行放國王的位置的正上方不能有國王,需滿足a\&b=0。

i滿ab1i1\qquadⅡ、第i行放國王的位置的斜上方不能有國王,需滿足a|b表示的二進制數不能有連續的1。\\\qquad這一條件同時也避免了第i行出現連續的1。

滿f[i][j][a]+=f[i1][jcount(a)][b]count(a)a1i\qquad若滿足以上兩個條件,轉移方程:f[i][j][a]+=f[i-1][j-count(a)][b],\\\qquad其中count(a)表示狀態a中1的個數,即第i行擺放國王的數量。

具體落實:

(1)1()①、先預處理每一行的所有合法狀態(不能含有連續的1),同時記錄下這些狀態當中1的個數(狀態轉移的時候要用)。

head②、將所有互不影響的狀態預處理出來,用head數組做一個映射。

③、計算每個狀態的方案總數。

注意:

0j0①、初始化邊界。對每一層而言,擺放的國王數量爲0時也是一種合法方案,因此國王數量j從0開始循環。

nf[n][m][s]n+1f[n+1][m][0]0②、最後要輸出第n層的方案總數之和,即\sum f[n][m][s],\\\qquad我們可以在計算時算到n+1層,最後輸出f[n+1][m][0],這是等價的。\\\qquad 因爲狀態爲0可由前一層的所有合法狀態轉移而來,相當於在計算方案數的過程中就做了一遍求和。
代碼:

#include<iostream>
#include<algorithm>
#include<vector>

#define ll long long

using namespace std;

const int N=12,M=1<<10,K=110;

int n,m;
ll f[N][K][M];
int cnt[M];
vector<int> state;
vector<int> head[M];

bool check(int x)
{
    for(int i=0;i<n-1;i++)
        if((x>>i)&1 && (x>>i+1)&1)
            return false;
    return true;
}

int Count(int x)
{
    int res=0;
    for(int i=0;i<n;i++) res+=(x>>i)&1;
    return res;
}

int main()
{
    cin>>n>>m;
    
    for(int i=0;i<1<<n;i++)
        if(check(i))
        {
            state.push_back(i);
            cnt[i]=Count(i);
        }
    
    int len=state.size();
    for(int i=0;i<len;i++)
        for(int j=0;j<len;j++)
        {
            int a=state[i],b=state[j];
            if((a&b)==0 && check(a|b))
                head[i].push_back(j);
        }
    
    f[0][0][0]=1;
    for(int i=1;i<=n+1;i++)
        for(int j=0;j<=m;j++)
            for(int a=0;a<len;a++)
                for(int b:head[a])
                {
                    int c=cnt[state[a]];
                    if(j>=c) f[i][j][state[a]]+=f[i-1][j-c][state[b]];
                }
            
    cout<<f[n+1][m][0]<<endl;
    
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章