原题传送门
作弊要警告处分的说。。。
显然是一道DP的题目
设计状态:
d
p
[
i
]
[
j
]
[
k
]
[
l
]
dp[i][j][k][l]
dp[i][j][k][l]表示第i道题目,用了j次偷看机会,两个人分别还剩
k
,
l
k,l
k,l道题目可以偷看
用刷表法转移,分类讨论
- 不偷看
- 只偷看左边:1、直接偷看;2、重新偷看
- 只偷看右边:1、直接偷看;2、重新偷看
- 两边一起偷看:1、都直接偷看;2、都重新偷看;3、左边那位直接偷看,右边那位重新偷看;4、左边那位重新偷看,右边那位直接偷看
注:直接偷看的意思是用了上次机会继续偷看下去,重新偷看的意思是重新新用一个机会开始偷看
转移的时候需要注意:
- 是否还能继续直接偷看
- 不管看不看,只要还在这次机会里面,都要使剩余的题目数-1,具体在代码中体现为 m a x ( k − 1 , 0 ) max(k-1,0) max(k−1,0)
来分析一下复杂度
时空间复杂度
O
(
n
p
k
2
)
O(npk^2)
O(npk2)
空间上发现第一位滚动掉就可以避免MLE
时间上怎么优化?
发现如果
p
∗
k
>
=
2
∗
n
p*k>=2*n
p∗k>=2∗n的话,你直接全部偷看过来得了
这个一特判,时间复杂度上界变为
O
(
n
2
k
)
O(n^2k)
O(n2k),就可以过啦
Code:
#include <bits/stdc++.h>
#define maxn 1010
#define maxm 60
using namespace std;
int n, p, K, a[maxn], b[maxn], dp[2][maxn][maxm][maxm];
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
void upd(int &x, int y){ if (x < y) x = y; }
int main(){
n = read(), p = read(), K = read();
int x = read();
for (int i = 1; i <= x; ++i){
int y = read(); a[y] = 1;
}
x = read();
for (int i = 1; i <= x; ++i){
int y = read(); b[y] = 1;
}
if (p * K >= (n << 1)){
int ans = 0;
for (int i = 1; i <= n; ++i) ans += a[i] | b[i];
return printf("%d\n", ans), 0;
}
memset(dp, -0x3f, sizeof(dp)); dp[0][0][0][0] = 0;
for (int i = 1; i <= n; ++i){
int now = i & 1, pre = now ^ 1;
memset(dp[now], -0x3f, sizeof(dp[now]));
for (int j = 0; j <= p; ++j)
for (int k = 0; k <= K; ++k)
for (int l = 0; l <= K; ++l){
int tmp = dp[pre][j][k][l];
upd(dp[now][j][max(k - 1, 0)][max(l - 1, 0)], tmp);
if (k) upd(dp[now][j][k - 1][max(l - 1, 0)], tmp + a[i]);
upd(dp[now][j + 1][K - 1][max(l - 1, 0)], tmp + a[i]);
if (l) upd(dp[now][j][max(k - 1, 0)][l - 1], tmp + b[i]);
upd(dp[now][j + 1][max(k - 1, 0)][K - 1], tmp + b[i]);
if (k && l) upd(dp[now][j][k - 1][l - 1], tmp + (a[i] | b[i]));
if (k) upd(dp[now][j + 1][k - 1][K - 1], tmp + (a[i] | b[i]));
if (l) upd(dp[now][j + 1][K - 1][l - 1], tmp + (a[i] | b[i]));
upd(dp[now][j + 2][K - 1][K - 1], tmp + (a[i] | b[i]));
}
}
int ans = 0;
for (int j = 0; j <= p; ++j)
for (int k = 0; k <= K; ++k)
for (int l = 0; l <= K; ++l) upd(ans, dp[n & 1][j][k][l]);
printf("%d\n", ans);
return 0;
}

本文探讨了一道经典的动态规划(DP)问题,通过设计四维状态dp[i][j][k][l]来解决竞赛中两人合作作弊的最优策略。文章详细分析了不同情况下的状态转移方程,并给出了优化后的O(n^2k)时间复杂度解决方案。
1208

被折叠的 条评论
为什么被折叠?



