洛谷 P1896 [SCOI2005]互不侵犯

题目描述

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

注:数据有加强(2018/4/25)

输入格式

只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

输出格式

所得的方案数

输入输出样例

输入 #1
3 2
输出 #1
16

思路:也是状压dp,每一行的状态不受行的限制,预处理出行的状态s[i],每一个状态安放了几个棋子c[i],dp[i][j][k]表示前i行,第i行状态是j时,已经放了k个棋子的种类数,dp[i][j][k]=sum(dp[i-1][m][k-c[i]]),s[j]&s[m] == 0, s[j]&s[m]>>1 == 0, s[j]&s[m]<<1 == 0,满足三个条件,说明第i-1行的状态不冲突
typedef long long LL;

const int maxm = (1<<10) + 5;

int s[maxm], c[maxm]; 
LL dp[10][maxm][85];
int n, k, cnt;

void dfs(int j, int state, int num) {
    if(j >= n) return;
    c[cnt] = num;
    s[cnt++] = state;
    for(int k = j+2; k < n; ++k) {
        if(!((1<<(k-1))&state)) dfs(k, state|(1<<k), num+1);
    }
}

int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n >> k; 
    for(int i = 0; i < n; ++i)
        dfs(i, 1<<i, 1);
    cnt++;
    for(int i = 0; i < cnt; ++i)
        dp[0][i][c[i]] = 1;
    for(int i = 1; i < n; ++i) {
        for(int j = 0; j < cnt; ++j) {
            for(int m = 0; m < cnt; ++m) {
                if(((s[j]&s[m]) || (s[j] & (s[m]>>1)) || (s[j] & (s[m]<<1))) == 0)
                    for(int x = k; x >= c[j]; x--)
                        dp[i][j][x] += dp[i-1][m][x-c[j]];
            }
        }
    }
    LL ans = 0;
    for(int i = 0; i < cnt; ++i)
        ans += dp[n-1][i][k];
    cout << ans << "\n";
    return 0;
}
View Code

 


posted @ 2020-01-29 20:40  GRedComeT  阅读(189)  评论(0编辑  收藏  举报