因為一個題目能否被做出實際上需要關注兩個人的情況,於是大抵是要記錄下兩個人的情況的。
於是一個想法就是 DP,把兩個人的情況放在狀態裡維護。
於是可以列出狀態,\(f_{i, j, x, y}\) 為到第 \(i\) 個人,還剩 \(j\) 次檢視的機會,第一 / 二個人檢視的連續段長度還有 \(x / y\)。
轉移就可以考慮列舉這一位是否要給每個人新開個長度為 \(k\) 的連續段,然後段長 \(-1\),統計這一位的貢獻。
然後會發現複雜度 \(\mathcal{O}(npk^2)\),很過不了的樣子。
那麼這個時候就可以考慮去減少無用狀態,這裡的無用狀態指的通常就是一定不優的狀態。
分析一下,就算每個人都全看,實際上的所需段數也為 \(\lceil\frac{n}{k}\rceil\),所以說一共所需的段數上界為 \(2\lceil\frac{n}{k}\rceil\),是 \(\mathcal{O}(\frac{n}{k})\) 級別的。
所以說減掉 \(p\) 無用的上界,時間複雜度 \(\mathcal{O}(n\min\{p, \frac{n}{k}\}k^2)\le \mathcal{O}(n^2k)\)。
#include<bits/stdc++.h>
inline void upd(int &x, int y) {
x < y && (x = y);
}
constexpr int inf = 0x3f3f3f3f;
const int maxn = 1e3 + 10, maxk = 50 + 2;
int a[maxn], b[maxn];
int f[maxn][maxk][maxk], g[maxn][maxk][maxk];
int main() {
int n, p, k;
scanf("%d%d%d", &n, &p, &k);
p = std::min(p, (n + k - 1) / k * 2);
for (int q, x, _ = scanf("%d", &q); q--; ) {
scanf("%d", &x), a[x] = 1;
}
for (int q, x, _ = scanf("%d", &q); q--; ) {
scanf("%d", &x), b[x] = 1;
}
memset(f, -0x3f, sizeof(f)), f[p][0][0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= p; j++) {
for (int x = 0; x < k; x++) for (int y = 0; y < k; y++) {
g[j][x][y] = f[j][x][y], f[j][x][y] = -inf;
}
}
for (int j = 0; j <= p; j++) {
for (int x = 0; x < k; x++) for (int y = 0; y < k; y++) {
for (int cx : {0, 1}) for (int cy : {0, 1}) {
if (cx + cy > j) continue;
int x_ = cx ? k : x, y_ = cy ? k : y;
upd(f[j - cx - cy][x_ ? (x_ - 1) : 0][y_ ? (y_ - 1) : 0],
g[j][x][y] + ((a[i] && x_) || (b[i] && y_)));
}
}
}
}
int ans = 0;
for (int j = 0; j <= p; j++) {
for (int x = 0; x < k; x++) for (int y = 0; y < k; y++) {
ans = std::max(ans, f[j][x][y]);
}
}
printf("%d\n", ans);
return 0;
}