51Nod - 1065 - 最小正子段和
N个整数组成的序列a11,a22,a33,…,ann,从中选出一个子序列(aii,ai+1i+1,…ajj),使这个子序列的和>0,并且这个和是所有和>0的子序列中最小的。
例如:4,-1,5,-2,-1,2,6,-2。-1,5,-2,-1,序列和为1,是最小的。
Input
第1行:整数序列的长度N(2 <= N <= 50000)
第2 - N+1行:N个整数
Output
输出最小正子段和。
Sample Input
8
4
-1
5
-2
-1
2
6
-2
Sample Output
1
这里有两种做法,一种是线段树,一种是贪心。
- 线段树:
这里还需要前缀和,先在输入的时候记录前缀和,用两个数组,然后其中一个进行排序,去重,离散化,建一棵树来记录区间内前缀和的最大值。接下来,按照原来没有排过序的前缀和从头到尾进行遍历,每一次都找到在当前前缀和位置之前的最大前缀和,这样做差能够得到相对最小的区间和,一直找最小值就是答案了。 - 贪心:
这个需要一个结构体,记录前缀和和该前缀和所处的位置,也是需要将前缀和进行排序,然后对排好序的前缀和进行遍历,如果后面的比前面的位置更大,那么就说明相对位置正确的前提下,他们的这段区间的和(或说这两个前缀和)更相近,也就是差值更小,如果相对位置不对的话就跳过了,这样遍历完后就是答案。
线段树AC代码:(借鉴学长)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn = 5e4 + 5;
ll sum[maxn], pre[maxn];
int n, m;
map<ll,int> mp;
struct node
{
int l;
int r;
ll val;
}p[maxn << 2];
void pushup(int cur)
{
p[cur].val = max(p[cur << 1].val, p[cur << 1 | 1].val);
}
void build(int l, int r, int cur)
{
p[cur].l = l;
p[cur].r = r;
p[cur].val = -inf;
if(l == r) return;
int m = (l + r) >> 1;
build(l, m, cur << 1);
build(m+1, r, cur << 1 | 1);
}
ll query(int pl, int pr, int cur)
{
ll res;
int l = p[cur].l, r = p[cur].r;
if(pl <= l && r <= pr)
{
return p[cur].val;
}
res = -inf;
if(pl <= p[cur << 1].r) res = max(res, query(pl, pr, cur << 1));
if(pr >= p[cur << 1 | 1].l) res = max(res, query(pl, pr, cur << 1 | 1));
return res;
}
void update(int tar,int cur)
{
int l = p[cur].l, r = p[cur].r;
if(l == r)
{
p[cur].val = pre[l];
return ;
}
int m = (l + r) >> 1;
if(tar <= m) update(tar, cur << 1);
else update(tar, cur << 1 | 1);
pushup(cur);
}
int main()
{
ll val, ans;
int i, l, r;
scanf("%d", &n);
for(i = 1; i <= n; i++)
{
scanf("%lld", &val);
sum[i] = sum[i - 1] + val;
pre[i] = sum[i];
}
mp.clear();
sort(pre+1, pre+n+1);
for(i = 1, m = 0; i <= n; i++)
{
if(!m || pre[m] != pre[i])
{
pre[++m] = pre[i];
mp[pre[m]] = m;
}
}
build(1, m, 1);
ans = inf;
for(i = 1; i <= n; i++)
if(sum[i] > 0) ans = min(ans, sum[i]);
for(i = 1; i <= n; i++)
{
l = 1, r = mp[sum[i]] - 1;
if(l <= r)
{
val = query(1, mp[sum[i]] - 1, 1);
if(val != -inf)
ans = min(ans, sum[i] - val);
}
update(mp[sum[i]], 1);
}
printf("%lld\n", ans);
return 0;
}
贪心AC代码(参考这篇博文)
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn = 5e4+10;
struct node
{
ll val;
int pos;
bool operator < (const node& tmp) const
{
if(val == tmp.val) return pos > tmp.pos;
return val < tmp.val;
}
}p[maxn];
int main()
{
int n, mx;
scanf("%d", &n);
p[0].val = 0;
p[0].pos = 0;
for(int i = 1; i <= n; i++)
{
scanf("%lld", &p[i].val);
p[i].val += p[i-1].val;
p[i].pos = i;
}
sort(p, p+n+1);
ll ans = 0;
for(int i = 1; i <= n; i++)
{
if(p[i].pos > p[i-1].pos)
{
if(!ans) ans = p[i].val - p[i-1].val;
else ans = min(ans, p[i].val - p[i-1].val);
}
}
printf("%lld\n", ans);
return 0;
}