[ACdream]娜娜梦游仙境系列——吃不完的糖果

Problem Description

娜娜好不容易才在你的帮助下”跳”过了这个湖,果然车到山前必有路,大战之后必有回复,大难不死,必有后福!现在在娜娜面前的就是好多好多的糖果还有一些黑不溜秋的东西!不过娜娜眼中只有吃不完的糖果!娜娜高兴地快要蹦起来了!

这时有一位挥着翅膀的女孩(天使?鸟人?)飞过来,跟娜娜说,这些糖果是给你的~(娜娜已经两眼放光)~你可以带走~(娜娜已经流下了口水)~但是~(神马?还有但是?)~这位神仙姐姐挥一挥翅膀~飘过了一片云彩,糖果和那些黑不溜秋的东西顿时飞了起来,落到地上成了摆成一个奇怪的图形。

神仙姐姐很满意,转过来对娜娜说:“这些糖果和黑洞(神马?黑洞?)分成n堆,每堆要么都是糖果,要么是黑洞,围成一个圈(即第1堆的旁边是第n堆和第2堆),你可以选择连续若干堆,然后带走,不过这些黑洞嘛,会馋嘴的小孩吸进去,你必须拿糖果去中和掉。”

娜娜喜欢糖果,但不喜欢动脑子~于是就把这个问题交给你,怎样才能让娜娜带走最多的糖果呢?

Input

多组数据,首先是一个正整数t(t<=20)表示数据组数
对于每组数据,包括两行,第一行是一个正整数n(3<=n<=100000)表示堆数
第二行是n个整数xi,如果是个正整数,则说明这是一堆数量为x[i]的糖果,如果是个负整数,则说明这是一个需要用abs(x[i])颗糖果去中和的黑洞。

Output

对于每组数据,输出一个整数,表示娜娜最多能带走的糖果数。

Sample Input

3
5
1 2 3 4 5
5
1 -2 3 -4 5
5
-1 -2 -3 -4 -5

Sample Output

15
7
0

Hint

对于样例1,娜娜可以把所有的糖果都拿走,所以输出15(=1+2+3+4+5)
对于样例2,娜娜可以拿走第1,2,3,5堆的糖果,别忘了这是摆成一个圈,所以输出7(=1+(-2)+3+5)
对于样例3,等待娜娜的是5个黑洞,可怜的娜娜,一个糖果都拿不掉,所以输出0
由于输入数据较多,请谨慎使用cin/cout

My Problem Report

这道题求的是一个环中的最大字串和,和HDU1003 Max Sum很相似。但此题一个错误的思路是将环展开,延长一倍。反例如下:

1 1 1 -1 -1 1

拓展后(2n-1):

1 1 1 -1 -1 1 [1] 1 1 -1 -1

输出中间结果(f[i])观察:

1 2 3 2 1 2 [1] 2 3 2 1

很明显可以看到第七位出现了错误。这是因为我在算法中加入了一个cnt数组在纪录当前串长度以避免长度超过n。我们来深入分析一下这个错误发生的原因:

当遍历到第七个点的时候,我们发现无法再继承上一个状态了,而当前状态也不是最优的。那如果我们在cnt达到n时比较一下当前点(1)与当前字串头(1),再决定是否继承呢?很显然这个决策也不是最优的,因为最优的决策是舍弃当前长度为6的字串的前5位,仅保留最后一位。也就是说,再这样的情境下,实际我们过去所作出的决策是有后效性的。

这个问题看起来就变得复杂了,但其实我们可以把它拆分为两个简单的子问题,再分别解决。

方法一:先求未拓展串中的最大字串和max1,再利用后缀和分别得未拓展串两端的最大字串和并合并得max2,最后max=Max{max1,max2}
方法二:原理与方法一相同,先求未拓展串中的最大字串和max1,再将原串所有元素取相反数,求得最大字串和max2’。但实际上,max2’是未拓展串中的最小字串和,不过当我们用未拓展串和sum减去它之后,就得到了方法一中的max2。综合起来max=Max{max1,sum-max2’}

方法一代码见下。

My Source Code

//  Created by Chlerry in 2015.
//  Copyright (c) 2015 Chlerry. All rights reserved.
//  http://acdream.info/contest?cid=1244#problem-C

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define ll long long

int t,n,cnt[200010];
ll a[200010],f[200010],l[200010],r[200010],maxn;

int main()
{
    freopen("in.txt","r",stdin);
    scanf("%d",&t);
for(int ca=1;ca<=t;ca++)
{
    memset(f,0,sizeof(f));
    memset(l,0,sizeof(l));
    memset(r,0,sizeof(r));
    scanf("%d",&n);
    maxn=0;
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)
    {
        if(f[i-1]+a[i]>a[i])
            f[i]=f[i-1]+a[i];
        else
            f[i]=a[i];
        if(maxn<f[i])
            maxn=f[i];
        l[i]=l[i-1]+a[i];
        r[n-i+1]=r[n-i+2]+a[n-i+1];
    }
    for(int i=1;i<=n;i++)
    {
        if(l[i]<l[i-1])
            l[i]=l[i-1];
        if(r[n-i+1]<r[n-i+2])
            r[n-i+1]=r[n-i+2];
    }
    for(int i=1;i<=n;i++)
        if(l[i]+r[i+1]>maxn)
            maxn=l[i]+r[i+1];
    printf("%lld\n",maxn);
}
    return 0;
}           
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值