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就过了。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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\n",Solve(lim));
}
return 0;
}
解法二: 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. 因为要保证中间过程不会心情有负的情况。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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\n",Solve(lim));
}
return 0;
}
Every day is meaningful, keeping learning!