codeforces 727F. Polycarp's problems

题目链接http://codeforces.com/contest/727/problem/F 
题目大意:有n个问题,每个问题有一个价值ai,一开始的心情值为q,每当读到一个问题时,心情值将会加上该问题的价值。问题只能按顺序读。有m个询问,求当q=bi时,至少要删去多少个问题才能使得在任何时候心情值都>=0

参考这篇:http://blog.csdn.net/aufeas/article/details/53031439

解法一: 贪心

分别求出最多删去i个问题需要的初始心情值的最小值f[i],最后在f数组上二分 求解答案。

利用贪心暴力计算f[i], 即如果当前心情小于0,就去掉 价值最小的问题。 

时间复杂度感人。。O(n^2*logn*log10^12+m*logn)

我一开始用了set,一直TLE, 改成priority_queue就过了。

代码:

#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;

#define N 800
typedef long long ll;

int n,m;
int a[N];
ll b[N];
set<int> st;

bool Check(ll lim,int k)
{
    priority_queue<int,vector<int>,greater<int> > q;
    int cnt=0;
    for (int i=1;i<=n;i++)
    {
        if (a[i]>=0) lim+=a[i];
        else
        {
            q.push(a[i]); lim+=a[i];
            if (lim<0)
            {
                lim-=q.top(); cnt++;
                q.pop();
                if (cnt>k) return false;
            }
        }
    }
    return true;
}

ll Calc(int k)
{
    ll L=0,R=1e12,Mid;
    while (L<R)
    {
        Mid=(L+R)>>1;
        if (Check(Mid,k)) R=Mid;
        else L=Mid+1;
    }
    return L;
}

int Solve(ll lim)
{
    int L=0,R=n,Mid;
    while (L<R)
    {
        Mid=(L+R)>>1;
        if (b[Mid]<=lim) R=Mid;
        else L=Mid+1;
    }
    return L;
}

int main()
{
    //freopen("in.in","r",stdin);
    //freopen("out.out","w",stdout);
    
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    
    for (int i=0;i<=n;i++) b[i]=Calc(i);
    
    ll lim;
    for (int i=1;i<=m;i++) 
    {
        scanf("%I64d",&lim);
        printf("%d
",Solve(lim));
    }
    
    return 0;
}
View Code

解法二: DP

这个dp有点巧妙,是从后往前的顺序。

dp[i][j]表示考虑i-n这些问题,最多只能去掉j个问题, 初始心情至少要多少。   如果求出dp数组,最后只要在dp[1]上二分求答案就好了。

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

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

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

代码:

#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;

#define N 800
typedef long long ll;

int n,m;
int a[N];
ll dp[N][N];

int Solve(ll lim)
{
    int L=0,R=n,Mid;
    while (L<R)
    {
        Mid=(L+R)>>1;
        if (dp[1][Mid]<=lim) R=Mid;
        else L=Mid+1;
    }
    return L;
}

int main()
{
    //freopen("in.in","r",stdin);
    //freopen("out.out","w",stdout);
    
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    
    for (int i=n;i>=1;i--)
    {
        for (int j=0;j<=n-i+1;j++)
        {
            if (a[i]<0) 
            {
                if (j) dp[i][j]=min(dp[i+1][j]-a[i],dp[i+1][j-1]);
                else dp[i][j]=dp[i+1][j]-a[i];
            }
            else dp[i][j]=dp[i+1][j]-a[i];
            if (dp[i][j]<0) dp[i][j]=0;
            
        }
    }

    ll lim;
    for (int i=1;i<=m;i++) 
    {
        scanf("%I64d",&lim);
        printf("%d
",Solve(lim));
    }
    
    return 0;
}
View Code

 

原文地址:https://www.cnblogs.com/vb4896/p/6099424.html