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;
}

 

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