P7689 [CEOI2002] Bugs Integrated,Inc. 题解
作者:互联网
Solution
感谢同学对这个题的精彩讲解!
设 \(f_{i,S_1,S_2}\) 表示处理完前 \(i\) 行,第 \(i+1\) 行的“突起”状态为 \(S_1\),第 \(i+2\) 行的“突起”状态为 \(S_2\)。
转移时,我们设第 \(i\) 行的状态为 \(S\),第 \(i-1\) 行 “突起”到第 \(\bm{i+1}\) 行 的状态为 \(S_0\),那么我们要从 \(f_{i-1,S,S_0}\) 转移到 \(f_{i,S_1,S_2}\):
\[f_{i,S_1,S_2}=f_{i-1,S,S_0}+\frac{|S_2|}2+\frac{|S_1\backslash S_2\backslash S_0|}3 \]注:\(S_2\) 即占据了 \(i\sim i+2\) 的 \(3\times 2\) 方块,\(|S_1\backslash S_2\backslash S_0|\) 即占据了 \(i\sim i+1\) 的 \(2\times 3\) 方块,可以自行画图理解。
于是这个东西时间复杂度太高,过不去。
注意到 \(S_0,S_2\) 中连续 \(1\) 的个数必须是偶数,\(|S_1\backslash S_2\backslash S_0|\) 中连续 \(1\) 的个数必须是三的倍数,我们可以只枚举这些符合要求的状态,这样可以节省很多无用枚举。
另外还有很多小的剪枝,比如 \(S_2\subseteq S_1\) 必须成立、\(S_0\subseteq S_1\backslash S_2\) 必须成立等等,具体见代码。
另外为了压空间,需要使用 uint8_t
、uint16_t
等类型。
Code
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
using namespace std;
#define count(x) (__builtin_popcount(x))
const int N = 150, M = 10;
int n, m, k;
uint8_t f[2][1 << M][1 << M];
uint16_t a[N + 10];
bool check(int x, int y) {
int cnt = 0;
for (int i = 0; i <= (1 << m) - 1; i++) {
if ((x >> i) & 1) cnt++;
else {
if (cnt % y) return 0;
cnt = 0;
}
}
return 1;
}
void mian() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= k; i++) {
int x, y; scanf("%d%d", &x, &y);
a[x] |= (1 << (y - 1));
}
uint16_t tot = (1 << m) - 1;
vector<uint16_t> ok1; // 2
a[n + 1] = a[n + 2] = tot;
set<uint16_t> ok2; // 3
for (uint16_t i = 0; i <= (1 << m) - 1; i++) {
if (check(i, 2)) ok1.push_back(i);
if (check(i, 3)) ok2.insert(i);
}
// msk0 msk msk1 msk2
for (int i = 1, ii = 1; i <= n; i++, ii = i % 2)
for (auto msk2 : ok1)
if ((msk2 & a[i + 2]) == 0)
for (uint16_t msk1 = 0; msk1 <= tot; msk1++)
if ((msk1 & msk2) == msk2 && (msk1 & a[i + 1]) == 0 && (msk1 & a[i]) == 0)
for (auto msk0 : ok1)
if ((msk0 & a[i]) == 0 && (msk0 & a[i - 1]) == 0 && ((msk1 - msk2) & msk0) == msk0 && ok2.find(msk1 - msk2 - msk0) != ok2.end())
for (uint16_t msk = 0; msk <= tot; msk++)
if ((msk & a[i]) == 0 && (msk & a[i - 1]) == 0 && ((msk1 - msk0) & msk) == 0 && (msk1 & msk0) == msk0)
f[ii][msk1][msk2] = max(f[ii][msk1][msk2], uint8_t(f[(ii + 1) % 2][msk][msk0] + count(msk2) / 2 + count(msk1 - msk2 - msk0) / 3));
printf("%d\n", int(f[n % 2][0][0]));
}
int main() {
int T; scanf("%d", &T);
while (T--) {
memset(f, 0, sizeof(f)); memset(a, 0, sizeof(a));
mian();
}
return 0;
}
标签:状态,cnt,int,题解,backslash,Bugs,P7689,突起,include 来源: https://www.cnblogs.com/registergen/p/p7689_solution.html