P2704 [NOI2001]炮兵阵地 (状压DP)
程序员文章站
2022-07-02 12:47:05
题目: "P2704 [NOI2001]炮兵阵地" 解析: 和互不侵犯一样 就是多了一格 用$f[i][j][k]$表示第i行,上一行状态为$j$,上上行状态为$k$的最多的可以放的炮兵 发现$100\times 1024\times 1024$开不下 还是通过简单的搜索发现就算$m==10$时合法 ......
题目:
解析:
和互不侵犯一样
就是多了一格
用\(f[i][j][k]\)表示第i行,上一行状态为\(j\),上上行状态为\(k\)的最多的可以放的炮兵
发现\(100\times 1024\times 1024\)开不下
还是通过简单的搜索发现就算\(m==10\)时合法的状态只有\(60\)种
\(100\times 60\times 60\)就没问题了
然后就和互不侵犯一样,枚举状态就可以了
状态转移
\(f[i][j][k] = max\{f[i][j][k], f[i-1][k][l]+sum[state[i]]\}\)
\(sum[i]\)表示\(i\)状态中有多少个\(1\)
代码:
#include <bits/stdc++.h> using namespace std; const int n = 110; int n, m, num, ans = -0x3f3f3f3f; int state[n], sum[1050], line[n], f[n][n][n]; char s[n]; int qpow(int a, int b) { int ans = 1; while (b) { if (b & 1) ans = ans * a; a *= a, b >>= 1; } return ans; } int main() { cin >> n >> m; for (int i = 1; i <= n; ++i) { cin >> s; for (int j = m - 1; j >= 0; --j) if (s[j] == 'p') line[i] += qpow(2, m - j - 1); } for (int i = 0; i < (1 << m); ++i) { if ((i & (i << 1)) || (i & (i >> 1)) || (i & (i << 2)) || (i & (i >> 2))) continue; state[++num] = i; sum[i] = __builtin_popcount(i); } for (int i = 1; i <= num; ++i) if ((state[i] | line[1]) == line[1]) f[1][i][1] = sum[state[i]]; for (int i = 2; i <= n; ++i) for (int j = 1; j <= num; ++j) if ((state[j] | line[i]) == line[i]) for (int k = 1; k <= num; ++k) if ((state[k] | line[i - 1]) == line[i - 1] && !(state[j] & state[k])) for (int l = 1; l <= num; ++l) if (!(state[l] & state[k]) && !(state[l] & state[j]) && (state[l] | line[i - 2]) == line[i - 2]) f[i][j][k] = max(f[i][j][k], f[i - 1][k][l] + sum[state[j]]); for (int i = 1; i <= num; ++i) for (int j = 1; j <= num; ++j) ans = max(ans, f[n][i][j]); cout << ans; }