bzoj1087 [SCOI2005]互不侵犯King

Posted zbtrs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1087 [SCOI2005]互不侵犯King相关的知识,希望对你有一定的参考价值。

1087: [SCOI2005]互不侵犯King

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 3306  Solved: 1915
[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
分析:这道题求方案数,那么可以想到用dp或者搜索,甚至还可以用数学方法,然而对于本题而言,搜索需要记录的信息和状态太多,会TLE,也没有什么很好的数学方法来解决这道题,所以只有采用dp.
       注意到n非常小,而且状态需要记录的信息非常多,所以考虑状态压缩dp.对于这类棋盘问题,当前行的状态和上一行的状态有着密切联系,而且答案和k有关,所以我们设计状态dp[i][j][k]为前i行已经放了j个国王并且第i行的状态为k(二进制)的方案数,那么dp[i][j][k] = Σdp[i-1][j - num[k]][p],其中num数组记录着一行为状态k的放的国王的数目,p为上一行符合要求的状态.
       现在的问题就是如何判断状态i是否符合要求,这不仅与i本身有关,还和它上一行的状态j有关,利用 <<,>>,&就可以判断了,它的原理是什么呢?我们把每个状态抽象成1个二进制数,它的第i位表示第i列放不放国王,利用&的性质,可以判断上下两行是否冲突,那么如何判断当前行是否冲突呢?将i左移1位再与i进行&操作即可.
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int n, k,num[1000];
long long dp[10][1000][1000];
bool flag[1000];
long long ans;

void init()
{
    for (int i = 0; i < (1 << n); i++)
        if (!(i & (i << 1)))
        {
            flag[i] = true;
            int t = i;
            while (t)
            {
                num[i] += (t & 1);
                t >>= 1;
            }
            dp[1][num[i]][i] = 1;
        }
}

int main()
{
    scanf("%d%d", &n, &k);
    init();
    for (int i = 2; i <= n; i++)
        for (int j = 0; j <= k; j++)
            for (int now = 0; now < (1 << n); now++)
            {
                if (!flag[now])
                    continue;
                if (num[now] > j)
                    continue;
                for (int last = 0; last < (1 << n); last++)
                {
                    if (!flag[last])
                        continue;
                    if ((last & now) || ((now << 1) & last) || ((now >> 1) & last))
                        continue;
                    dp[i][j][now] += dp[i - 1][j - num[now]][last];
                }
            }
    for (int i = 0; i < (1 << n); i++)
        ans += dp[n][k][i];

    printf("%lld", ans);
    return 0;
}

 

以上是关于bzoj1087 [SCOI2005]互不侵犯King的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1087: [SCOI2005]互不侵犯King

BZOJ 1087: [SCOI2005]互不侵犯King [状压DP]

BZOJ 1087 [SCOI2005]互不侵犯King (状压DP)

BZOJ 1087 [SCOI2005]互不侵犯King

bzoj 1087 [SCOI2005] 互不侵犯King

bzoj 1087: [SCOI2005]互不侵犯King