Codeforces Round #408 E. Exam Cheating DP

Description
有n道题目,有两个人分别会做某些题目,有p次偷看机会,每次可以偷看某个人最多连续k道题目,求最多偷看几道题目。(注意:是可以重叠的)


Sample Input
6 2 3
3 1 3 6
4 1 2 5 6


Sample Output
4


你设f[i][j][x][y]为到第i道题目,已看了j次,对第一个人还剩x次机会,对第二个人还剩y次机会。
反三种情况转移即可。
有一个优化,如果 p > = 2 ∗ ( n + K − 1 ) / K p >= 2 * (n + K - 1) / K p>=2(n+K1)/K你就直接算。
唉,状态有点繁琐,真是烦死我了。。。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
int _min(int x, int y) {return x < y ? x : y;}
int _max(int x, int y) {return x > y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

int a[1100], b[1100];
int f[2][1010][51][51];
int s1[1100], s2[1100];

int main() {
	int n = read(), p = read(), K = read();
	int h1 = read(); for(int i = 1; i <= h1; i++) a[i] = read(), s1[a[i]] = 1;
	int h2 = read(); for(int i = 1; i <= h2; i++) b[i] = read(), s2[b[i]] = 1;
	int ans = 0;
	if(p >= 2 * (n + K - 1) / K) {
		for(int i = 1; i <= n; i++) if(s1[i] || s2[i]) ans++;
		printf("%d\n", ans);
		return 0;
	}
	int now = 0; memset(f[now], -1, sizeof(f[now]));
	f[now][0][0][0] = 0;
	for(int i = 1; i <= n; i++) {
		now ^= 1; memset(f[now], -1, sizeof(f[now]));
		for(int j = 0; j <= p; j++) {
			for(int x = 0; x <= K; x++) {
				for(int y = 0; y <= K; y++) {
					if(j <= p - 2) {
						f[now][j + 2][K - 1][K - 1] = _max(f[now][j + 2][K - 1][K - 1], _max(f[now ^ 1][j][x][y], 0) + (s1[i] | s2[i])), 
						ans = _max(ans, f[now][j + 2][K - 1][K - 1]);
					}
					if(j <= p - 1) {
						if(y && f[now ^ 1][j][x][y] != -1) f[now][j + 1][K - 1][y - 1] = _max(f[now][j + 1][K - 1][y - 1], _max(f[now ^ 1][j][x][y], 0) + (s1[i] | s2[i])), 
						ans = _max(ans, f[now][j + 1][K - 1][y - 1]);
						else f[now][j + 1][K - 1][0] = _max(f[now][j + 1][K - 1][0], _max(f[now ^ 1][j][x][0], 0) + s1[i]), 
						ans = _max(ans, f[now][j + 1][K - 1][0]);
						if(x && f[now ^ 1][j][x][y] != -1) f[now][j + 1][x - 1][K - 1] = _max(f[now][j + 1][x - 1][K - 1], _max(f[now ^ 1][j][x][y], 0) + (s1[i] | s2[i])), 
						ans = _max(ans, f[now][j + 1][x - 1][K - 1]);
						else f[now][j + 1][0][K - 1] = _max(f[now][j + 1][0][K - 1], _max(f[now ^ 1][j][0][y], 0) + s2[i]), 
						ans = _max(ans, f[now][j + 1][0][K - 1]);
					} int hh = 0;
					if(f[now ^ 1][j][x][y] != -1) {
						if(x) hh |= s1[i]; if(y) hh |= s2[i];
						f[now][j][_max(0, x - 1)][_max(0, y - 1)] = _max(f[now][j][_max(0, x - 1)][_max(0, y - 1)], f[now ^ 1][j][x][y] + hh);
						ans = _max(ans, f[now][j][_max(0, x - 1)][_max(0, y - 1)]);
					}
				}
			}
		}
	} printf("%d\n", ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值