Codeforces Round #408 (Div. 2) E

24 篇文章 0 订阅

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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值