[BZOJ1087][SCOI2005]互不侵犯King

1087: [SCOI2005]互不侵犯King

Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 4255  Solved: 2458 [Submit][Status][Discuss]

Description

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

Input

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

Output

  方案数。

Sample Input

3 2

Sample Output

16
 
暴力枚举铁定TLE,考虑状压
设$dp[i][j][k]$表示前$i$行共取了$j$个且第$i$行取的情况为$k$的合法方案数
然后预处理出每种取法的棋子数,以及两两之间是否可行,转移即可
 
 
#include <iostream>
using namespace std;
int n, k, Max;
int cnt[512] = {0};
bool s1[512], s2[512][512];
void init(){
    for(int i = 0; i <= Max; i++){
        if(i & (i >> 1)){
            s1[i] = false;
            continue;
        }
        s1[i] = true;
        for(int j = 0; j < 9; j++)
            if(i & (1 << j)) cnt[i]++;
    }
    for(int i = 0; i <= Max; i++)
        for(int j = i; j <= Max; j++)
            if((i & (j >> 1)) || (i & j) || ((i >> 1) & j)) s2[i][j] = s2[j][i] = false;
            else s2[i][j] = s2[j][i] = true;
}
long long dp[10][100][512] = {0};
int main(){
    cin >> n >> k;
    Max = (1 << n) - 1;
    init();
    for(int i = 0; i <= Max; i++)
        if(s1[i] && cnt[1] <= k) dp[1][cnt[i]][i] = 1;
    for(int i = 1; i < n; i++)
        for(int j = 0; j <= Max; j++) if(s1[j])
            for(int l = 0; l <= Max; l++) if(s1[l] && s2[j][l])
                 for(int m = cnt[j]; m + cnt[l] <= k; m++)
                     dp[i + 1][m + cnt[l]][l] += dp[i][m][j];
    long long ans = 0;
    for(int i = 0; i <= Max; i++)
        if(s1[i]) ans += dp[n][k][i];
    cout << ans << endl;
    return 0;
}

 

posted @ 2017-08-27 16:55  jzyy  阅读(161)  评论(0编辑  收藏  举报