嘘~ 正在从服务器偷取页面 . . .

洛谷P1896 [SCOI2005] 互不侵犯 题解


洛谷P1896 [SCOI2005] 互不侵犯 题解

题目链接:P1896 [SCOI2005] 互不侵犯

题意

在 $n \times n$ 的棋盘里面放 $K$ 个国王,并使他们互不攻击,求合法摆放方案数。

国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共 $8$ 个格子。

输入格式

一行,包含两个数 $n,k~(1 \le n \le 9, 0 \le K \le n^2)$

输出格式

所得的方案数。

退役后的第一篇题解,作为复习(?

简单的状压dp。设 $f_{i,j,k}$ 表示第 $i$ 行的状态为 $j$ ( $j$ 必须自身合法 ),且已经用了 $k$ 个国王的方案数

其中 $l$ 为上一行的状态,且 $l,j$ 没有“相接”的地方,$\mathrm{popc}(j)$ 表示这一行放了 $j$ 个国王。

答案就是 $\sum f_{n,i,K}$ ,其中 $i$ 为所有合法的状态。

时间复杂度 $\mathcal{O}(nK4^n)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define popc(a) __builtin_popcountll(a)
#define N ((int)())

int n,K,cnt,popc[1 << 9],f[11][1 << 9][105];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> K; int mx = (1ll << n) - 1, res = 0;
    for(int i=0; i<=mx; i++) f[1][i][popc(i)] = 1;
    for(int i=2; i<=n; i++)
        for(int j=0; j<=mx; j++) if(!(j & (j << 1)))
            for(int k=popc(j); k<=K; k++)
                for(int l=0; l<=mx; l++) if(!(l & (l << 1)) && (!(j & l)))
                {
                    if((!(l & (j << 1))) && (!(l & (j >> 1))))
                        f[i][j][k] += f[i - 1][l][k - popc(j)];
                }
    for(int i=0; i<=mx; i++) if(!(i & (i << 1))) res += f[n][i][K];
    cout << res << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录