AcWing 1064. 小国王

状态:f[i][j][k]表示第i行放了j个皇帝,状态为k的方案。

那么首先预处理出所有可行的方案,以及两两可以相互转移的答案。

b状态转移到a状态就是 :\(f[i][j][a] += f[i - 1][j - count(a)][b]\)

小tips:

判断两行有没有相邻的一可以判断: \((a \,\, \& \,\, b) == 0\)\(a \,\, \& \,\, b\)一定要加括号,判断当两个在前后两行的皇帝是否能斜着攻击到:即\(a | b\)不能有相邻的1

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

using namespace std;

typedef long long LL;

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

LL f[N][M][K]; //第i行 放了j个皇帝 状态为k的方案集合
int n, m;
vector<int> legal;
int cnt[K + 10];
vector<int> head[K];

bool check(int num) { //没有相邻
    for (int i = 0; i < n; i++) {
        if ((num >> i & 1) && ((num >> i + 1) & 1)) return false;
    }
    
    return true;
}

int count(int num) {
    int cnt = 0;
    while (num) num -= (num & -num), cnt++;
    return cnt;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < 1 << n; i++) {
        if (check(i)) {
            legal.push_back(i);
            cnt[i] = count(i);
        }
    }
    
    for (int i = 0; i < legal.size(); i++) {
        for (int j = 0; j < legal.size(); j++) {
            int a = legal[j], b = legal[i];
            if ((a & b) == 0 && check(a | b)) 
                head[i].push_back(j); //由j可以转移到i
        }
    }
    
    f[0][0][0] = 1;
    for (int i = 1; i <= n; i++) { //枚举行
        for (int j = 0; j <= m; j++) { //枚举有多少个皇帝
            for (int a = 0; a < legal.size(); a++) { //枚举可转移的状态
                for (int b = 0; b < head[a].size(); b++) {
                    int c = cnt[legal[a]];
                    if (j >= c) f[i][j][a] += f[i - 1][j - c][head[a][b]];
                }
            }        
        }
    }
    
    LL res = 0;
    for (int i = 0; i < 1 << 10; i++) res += f[n][m][i];
    
    cout << res << endl;
    //cout << f[n + 1][m][0] << endl;
    
    return 0;
}
posted @ 2021-06-04 08:34  Xxaj5  阅读(43)  评论(0编辑  收藏  举报