CodeForces-796E Exam Cheating 【DP】

题目链接

题意

在两个数轴上有一些整数(n<1000),用p个(p<1000)个长度小于等于k(k<=50)的区间去覆盖这些整数,求最多多少个整数能够被覆盖(相同的不重复计数)

分析

易想到这个四维DP的状态:
\[dp[n][p][r1][r2] \leftrightarrow 到n用了p个区间,上面覆盖的最后一个区间的长度为r1,下面的覆盖的最后一个区间的长度为r2\]

直接开这个数组,1000*1000*50*50会炸,通过下面的转移方程可以发现对n可以滚动数组优化。
至于转移方程就比较麻烦了,主要是考虑三种情况:

  1. 直接在前两个区间后面再加一个
  2. 新开一个区间
  3. 新开两个区间
    具体如何转移可以看下面的代码(可能写得比较繁琐,然而我觉得标程比我写得还繁琐),其中新开区间需要记录上一个位置的最大值,同样注意通过记录最大值来优化。

这样之后仍然无法再2s内跑完(\(O(npk^2)\) 2e9的复杂度)这里需要一个特别的优化:如果区间多到可以将n以内的每个数都能覆盖,则直接\(O(n)\)就可以算出答案。也就是说,如果\(p>\lceil \frac{n}{k} \rceil\)就不需要跑dp了,而剩下需要dp的情况最多为1000*1000*50就可以跑完

AC代码

//CodeForces-796E Exam Cheating
//AC 2017-4-12 18:41:19 by Carl Luo
//DP
#include 
using namespace std;
const int maxn=1e3+100;

int n,p,k;
int dp[2][maxn][55][55];
int maks[2][2][maxn][55];
int tmaks[2][maxn];
int question[2][maxn];

int main()
{
    scanf("%d %d %d",&n,&p,&k);
    int cnt,x;
    int res=0;
    for(int i=0;i<2;++i)
    {
        scanf("%d",&cnt);
        while(cnt--)
        {
            scanf("%d",&x);
            question[i][x]=1;
        }
    }
    if(p>2*ceil(n/k))
    {
        for(int i=1;i<=n;++i)
            res+=(question[0][i]||question[1][i]);
        cout<1&&r2>1&&j>=2)
                        cur=max(cur,dp[!now][j][r1-1][r2-1]+(question[0][i]||question[1][i]));
                    if((r1==0||r1==1)&&j>=r1&&!(r2==0||r2==1))
                        cur=max(cur,maks[!now][0][j-r1][r2-1]+(question[1][i]||(question[0][i]&&r1)));
                    if((r2==0||r2==1)&&j>=r2&&!(r1==0||r1==1))
                        cur=max(cur,maks[!now][1][j-r2][r1-1]+((question[1][i]&&r2)||question[0][i]));
                    if((r1==0||r1==1)&&(r2==0||r2==1)&&j>=r1+r2)
                        cur=max(cur,tmaks[!now][j-r1-r2]+((question[0][i]&&r1)||(question[1][i]&&r2)));
                    //cout<

转载于:https://www.cnblogs.com/DrCarlluo/p/6700799.html

你可能感兴趣的:(CodeForces-796E Exam Cheating 【DP】)