Solution - Codeforces 796E Exam Cheating

rizynvu發表於2024-11-01

因為一個題目能否被做出實際上需要關注兩個人的情況,於是大抵是要記錄下兩個人的情況的。

於是一個想法就是 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;
}

相關文章