[SCOI2005]互不侵犯

洛谷

简化题意

让你在一个棋盘上放置 k 个棋子,每个棋子的八个方位不能有其他的棋子。


思路

\(f_{i, j, s}\) 表示,第 i 行放置的方式为 j,放置了多少个棋子。
\(sit_x\) 表示 x 中 1 的个数,可以预处理,以及当前状态下放置的国王的个数 gs 。

那么 \(f_{i,j,s} = \sum f_{i-1, k, s - gs_j}\)

转移的条件就是:

  • \(sit_j \ \& \ sit_k\) 及上下有重复的棋子。
  • \((sit_j << 1) \& sit_k\) 及左上右下有重复的棋子。
  • \(sit_j \& (sit_k << 1)\) 及左下右上有重复的棋子。

以上条件如果都不满足,那么可以转移。

code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define N 110
#define M 20

using namespace std;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
int n, k, s0;
ll f[20][N][110], num[N], s[N];

int read() {
  int s = 0, f = 0; char ch = getchar();
  while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
  while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
  return f ? -s : s;
}

int main() {
  n = read(), k = read();
  for (int i = 0; i < (1 << n); i++) {
    if (i & (i << 1)) continue;
    int kk = 0;
    for (int j = 0; j < n; j++)
      if (i & (1 << j)) kk++;
    s[++s0] = i, num[s0] = kk;
  }
  f[0][1][0] = 1;
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= s0; j++)
      for (int kk = num[j]; kk <= k; kk++)
        for (int t = 1; t <= s0; t++)
        if (!(s[t] & s[j]) && !(s[t] & (s[j] >> 1)) && !(s[t] & (s[j] << 1)))
          f[i][j][kk] += f[i - 1][t][kk - num[j]];
  ll ans = 0;
  for (int i = 1; i <= s0; i++) ans += f[n][i][k];
  cout << ans;
}
posted @ 2020-11-12 08:25  Kersen  阅读(89)  评论(0)    收藏  举报