P1896 [SCOI2005]互不侵犯(狀壓DP)

https://www.luogu.org/problemnew/show/P1896

dp[i][j][l] 三維狀態

i表示第幾行  j表示狀態,l表示所用國王數

題中給了n最大爲9 那麼狀態數就是1<<9 

我們先預處理所需的數據,即某一狀態是否合法,及當前狀態所需的國王數

上下行需要滿足 (h&j) == 0 && ((h<<1)&j) == 0 && ((h>>1)&j) == 0 即 國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各一個格子

然後疊加就可以了  記得開longlong 否則數據會爆int

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 100;
const int M = 1e7 + 100;
const int INF = 0x3f3f3f3f;
const int mod = 100000000;
ll n,k,dp[20][(1<<10)+100][110],need[(1<<10)+100],vis[(1<<10)+100];
int main()
{
    cin >> n >> k;
    //預處理每種狀態所需的國王數
    for(int i = 0;i < (1<<n);i ++){
        int t = i;
        while(t){
            if(t&1) need[i] ++;
            t >>= 1;
        }
    }
    //預處理合適的狀態
    for(int i = 0;i < (1<<n);i ++) if(((i<<1)&i) == 0) vis[i] = 1;
    //預處理第一行
    for(int i = 0;i < (1<<n);i ++) if(vis[i] && need[i] <= k) dp[1][i][need[i]] = 1;
    for(int i = 2;i <= n;i ++){
        for(int j = 0;j < (1<<n);j ++){
            if(vis[j]){
                for(int h = 0;h < (1<<n);h ++){
                    if(vis[h] && (h&j) == 0 && ((h<<1)&j) == 0 && ((h>>1)&j) == 0){
                        for(int len = k;len >= need[j];len --)
                            dp[i][j][len] += dp[i-1][h][len-need[j]];
                    }
                }
            }
        }
    }
    ll ans = 0;
    for(int i = 0;i < (1<<n);i ++) ans += dp[n][i][k];
    cout << ans;
    return 0;
}

 

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