Codeforces Round #408 (Div. 2) E

xiaoxiao2021-02-28  111

E. Exam Cheating

题目链接:E. Exam Cheating 题意是有个女同学要作弊,总共有n道题,旁白有两位学霸,但是他们分别完成了r道题和s道题(按题号严格递增)。这位同学要作弊总共能看p次,每次只能看一位同学的最多连续k道题,只有学霸完成了的题目才能看。问最多能看多少道题。

感觉很巧妙的dp题,一开始我以两位学霸完成了题目为时间轴,但是后来发现有相互覆盖的情形。看了别人的状态后,是这样的:以题目为时间轴,dp[i][a][b][x]代表前i道题看了x眼且第一个学霸还剩下a道题,第二个学霸剩下b道题可以看。 解释一下a b 这个状态吧,我也是看挺久才理解的。 以第一个样例为例 6 2 3 3 1 3 6 4 1 2 5 6 总共有 1 2 3 4 5 6 六道题(加粗代表学霸已经完成的题目) 第一个1 2 3 4 5 6 第二个1 2 3 4 5 6 那么由于每次能最多看连续k个题,毫无疑问我们肯定就看k个题 于是假设在第二题处看了第二个人一眼 状态就是dp[2][0][2][1] ,除去了当前看这道题,对于第二个人我们还能往后面再看2个题 假设在第一道题处看了第一个人一眼,那么就是dp[1][2][0][1] 然后说一下转移,由于每次可以选择看或者不看 假设不看 dp[i][a][b][x] = max(dp[i-1][a+1][b+1][x] , dp[i][a][b][x]) 如果看呢 比如看了第一个人,那么第一个人肯定还会剩下k-1道题可以看。 dp[i][k-1][b][x] = max(dp[i][a][b][x] , dp[i-1][a][b+1][x - 1]) 看了第二个人也同理 还有就是当a b 等于0的时候要特殊处理,a b等于0的时候仍然可以转移,但是方程就要变动一下了由于比较复杂直接贴代码吧 数据范围比较大避免MLE就用滚动数组来代替

#include<cmath> #include<algorithm> #include<cstring> #include<string> #include<set> #include<map> #include<time.h> #include<cstdio> #include<vector> #include<list> #include<stack> #include<queue> #include<iostream> #include<stdlib.h> using namespace std; #define LONG long long const int INF=0x3f3f3f3f; const LONG MOD=1e9+ 7; const double PI=acos(-1.0); #define clrI(x) memset(x,-1,sizeof(x)) #define clr0(x) memset(x,0,sizeof x) #define clr1(x) memset(x,INF,sizeof x) #define clr2(x) memset(x,-INF,sizeof x) #define EPS 1e-10 #define lson l , mid , rt<< 1 #define rson mid + 1 ,r , (rt<<1)+1 #define root 1, m , 1 int A[5010] ; int B[5010] ; int dp[2][55][55][500] ; int ab(int x) { if(x < 0 )return 0 ; return x; } int Cei(int a , int b) { if(a % b != 0) return a/b + 1; return a / b; } int main() { int n , p , k ; clr0( A ) ; clr0( B ) ; clr2( dp ) ; cin >> n >> p >>k ; if( p > 2 * Cei(n , k) ) p =2 * Cei(n,k) ; int xx ; int tt ; cin >> tt ; for(int i = 1 ; i <= tt ;++i) cin >> xx , A[xx] = 1 ; cin >> tt ; for(int i = 1 ; i <= tt ; ++ i) cin >> xx , B[xx] = 1 ; int pre = 0 , now = 1; dp[0][0][0][0] = 0 ; int ans = 0 ; for(int i = 1 ; i <= n ; ++ i) { for(int x = 0 ; x <= p ; ++ x) { for(int a = 0 ; a <= k - 1 ; ++ a) { for(int b = 0 ; b <= k - 1 ; ++ b) { if(b != 0 && a== 0) { dp[now][k-1][(b-1)][x +1] = max(dp[now][k-1][(b-1)][x+1], dp[pre][a][b][x] + (A[i] | B[i]) ); dp[now][a][b-1][x] = max(dp[now][a][b-1][x] , dp[pre][a][b][x] + B[i]) ; dp[now][a][k-1][x+1] = max(dp[now][a][k-1][x+1] , dp[pre][a][b][x] + B[i]) ; } else if( b == 0 && a == 0) { dp[now][k-1][b][x+1] = max(dp[now][k-1][b][x+1] , dp[pre][a][b][x] + A[i] ) ; dp[now][a][b][x] = max(dp[now][a][b][x] , dp[pre][a][b][x] ) ; dp[now][a][k-1][x+1] = max(dp[now][a][k-1][x+1] , dp[pre][a][b][x] + B[i] ) ; } else if(a != 0 && b != 0) { dp[now][(a-1)][ k - 1][x +1] = max(dp[now][(a - 1)][k-1][x+1], dp[pre][a][b][x] + ((A[i]) || (B[i]))); dp[now][a-1][b-1][x] = max(dp[now][a-1][b-1][x] , dp[pre][a][b][x] + (A[i] || B[i])) ; dp[now][k-1][b-1][x+1] = max(dp[now][k-1][b-1][x+1] , dp[pre][a][b][x] + ( A[i] || B[i]) ) ; } else if(a!= 0 && b == 0) { dp[now][a-1][b][x] = max(dp[now][a-1][b][x] , dp[pre][a][b][x] + A[i]) ; dp[now][a-1][k- 1][x+1] = max(dp[now][a-1][k-1][x+1] , dp[pre][a][b][x] + (A[i] | B[i])) ; dp[now][k-1][b][x + 1] = max(dp[now][k-1][b][x+1] , dp[pre][a][b][x] + A[i] ) ; } } } } clr2(dp[pre]) ; now ^= 1 ; pre ^= 1; } for(int i = 0 ; i <= p ; ++ i) { for(int a = 0 ; a <= k ; ++ a) for(int b = 0 ; b <= k ; ++ b)ans = max(ans , dp[pre][a][b][i]) ; } cout<<ans <<endl; }
转载请注明原文地址: https://www.6miu.com/read-30034.html

最新回复(0)