这篇题解可能会有重复的地方,但我会把我做这道题所学到的和所研究的分享给大家,建议新手观看。大佬跳过
首先,了解一下题目中出现的关键词:子序列和前缀和。
子序列
子序列就是在原来序列中找出一部分组成的序列。
要注意的是,子序列是由原来序列中符合条件的元素按从前往后的顺序添加到新序列中所组成的序列,顺序是不能变的。比如说有一个序列 12345 1 2 3 4 5 12345,它的子序列可以是 124 124 124,也可以是 135 135 135,但不可以是 231 231 231,也不可以是 342 342 342。
前缀和
前缀和是一个数组的某个元素的下标之前(包括此元素)的所有数组元素的和。
这道题知道这些前缀和知识就足够了,如果还想了解更多,可以去这位大佬的博客学习:
https://www.cnblogs.com/-Ackerman/p/11162651.html#
知道了这些,就可以很轻松地把这道题切掉了~~
这道题的大体意思就是给出一个序列,求出最长子序列使得这个子序列的每一个元素的前缀和均不为负。
思路
这道题我用的是贪心,非负数一定要在这个子序列中,至于负数怎么选,要紧扣子序列定义来做,我们可以建一个优先队列,并按顺序依次将原序列的元素取反入队(优先队列优先弹出队列中数值大的元素),并且用一个累加器累加,当这个累加器变为负数时,弹出栈顶元素,其实就是当时最小的负数,如果子序列中有这个数,那么这个方案一定不是最优,所以必须舍弃,到最后,看一看舍弃了几个数,用原序列个数减去即为最终答案。
看数据范围,还是开 long long 比较保险。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
int n;
ll f[2010],tot,t,p,sum;
priority_queue<ll> q;//优先队列
int main()
{
scanf("%d",&n);
tot=n;//先记录原序列个数
for(int i=1;i<=n;i++)
scanf("%lld",&f[i]);
for(int i=1;i<=n;i++){
sum+=f[i];//累加器不断累加
q.push(-f[i]);//取反入队
if(sum<0){//累加器小于0,要删元素了
p=q.top();
q.pop();
p=-p;//变回原来的数值
sum-=p;//清除这个非最优选择
t++;//统计删除数字的个数
}
}
printf("%lld",tot-t);//总数减删除数字的个数即为答案
return 0;
}
完结撒花~~