CF727F Polycarp's problems

题面大意:有n个问题,每个问题有一个品质ai,一开始的心情值为q,每当看完一个问题时,心情值将会加上这道问题的品质。问题只能按顺序看。有m个询问,求当q=bi时,至少要删去多少个问题才能使得在任何时候心情值都>=0。

这道题由于m的范围很大,对于询问我们不可能每次都重做一遍,但是n的范围只有750,因此我们可以通过预处理减少时间复杂度。

一般的dp方程都会设f[i][j]表示前i个食物留下j个需要的最小心情值,不过因为有不能低于0的限制,这样做就需要保证过程中的心情值不会低于0并且还要知道目前的心情值,这很麻烦,我们不妨倒着考虑,用f[i][j] 表示从第i个食物到第n个食物保留j个需要的最小心情值,这样一来就只需要知道过程中的心情值即可

我们对于每个ai进行分类讨论

如果a[i]>=0, 那么a[i]这个问题肯定没必要删掉, f[i][j]=f[i+1][j]-a[i];

如果a[i]<0 , f[i][j]=min(f[i+1][j]-a[i] ,  f[i+1][j-1]);  分别是删掉a[i]和不删的情况。

如果算出来f[i][j]<0, 那么f[i][j]=0.    因为要保证中间过程不会心情有负的情况。

总结一下,

f[i][j]=min(f[i+1][j],max(0,f[i+1][j−1]−a[i]))

根据定义,f[1][j] 就是从所有食物中留下j个需要的最小心情值

j 越大,f[1][j]就要越大,这是一个非递减序列,我们便可以在最后的f[1]上二分查找

总的时间复杂度为 O(n^2+m*log n)

CODE

#include<bits/stdc++.h>
using namespace std;
#define LL long long 
#define INF 0x3f3f3f3f3f3f3f3f
#define N 2000005
#define maxn 1005
void read(LL &x){
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m;
LL a[maxn];
LL f[maxn][maxn];
int main(){
    scanf("%d%d",&n,&m); 
    for(int i=1;i<=n;++i)
       read(a[i]);
    memset(f,0x3f,sizeof(f));
    f[n+1][0]=0;
    for(int i=n;i;--i)
       for(int j=0;j<=n-i+1;++j){
            f[i][j]=f[i+1][j];
            if(j) f[i][j]=min(f[i][j],max(0ll,f[i+1][j-1]-a[i]));//转移方程 
       }
    for(int i=1;i<=m;++i){
        LL x;
        read(x);
        printf("%d\n",n-int(upper_bound(f[1],f[1]+n+1,x)-f[1]-1));//二分 
    }
    return 0;
}

 

posted @ 2022-02-13 11:01  LikC1606  阅读(66)  评论(0)    收藏  举报