【题解】CF796E:Exam Cheating

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

原题传送门
作弊要警告处分的说。。。
显然是一道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(k1,0)

来分析一下复杂度
时空间复杂度 O ( n p k 2 ) O(npk^2) O(npk2)
空间上发现第一位滚动掉就可以避免MLE
时间上怎么优化?
发现如果 p ∗ k > = 2 ∗ n p*k>=2*n pk>=2n的话,你直接全部偷看过来得了
这个一特判,时间复杂度上界变为 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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值