Sicily 1091 Maximum Sum(SOJ 1091)【dp动态规划】

来源:互联网 发布:美科8652电子琴编程 编辑:程序博客网 时间:2024/05/20 03:43

原题地址:点击打开链接

题目之前一直有打算做,但是到近来才马力去想,可惜的是没想到呃

肯定很多人觉得这题很水,有很多人第一眼看到答案,或者做出来之后,就忽然觉得这题很水····

可惜我天资不太行,虽然写完后代码只有寥寥几十行,但还是没觉得它水····


————正题————

以sample input为例,对于这个序列:

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

有一个还算比较容易想到的是:要将序列分成左边和右边两个部分。

应当注意到两个部分的特征是(假设左边为A,右边为B),A部分的某个连续字串构成了A的最大连续和(记为AM),B的同理。最终结果则是AM+BM。

对于整个字符串来说,如果有N个数字,那么可以划分的情况为N-1种,在N-1种情况中挑最大的即可。

现在问题就在于怎么找到AM和BM,以A={3, 2, -1, 2, 4}为例子,显然仅仅比较连续的整数和(即3+2与2+4)是不行的,因为3+2+(-1)+2+4的结果是最大的,然而如果对于A={3, 2, -100, 2, 4},那么很明显AM=6。如何知道碰到一个负数的时候是应该把它也先加上(如第一种情况),还是把它抛弃掉(如第二种情况)呢?

上面那个问题就是当时我的思考瓶颈,在继续谈上面那个问题之前,我想说一个很关键的思考方法,对于解这道题,我觉得一直想着怎么去找子串可能会有所困难。但是如果能够想到为每一个下标加2个属性:1.从左边到当前位置,最大的连续和是多少    2.从右边到当前位置,最大连续和是多少。好了继续说题

碰到负数时,如果当前的连续和的绝对值小于或等于该负数的绝对值,那么就丢弃掉那个负数,并将当前连续和设置为0,在下一个位置重新计算连续和。否则,如果没有大于,那么可以先加上该负数。再计算过程中,每次移动到新下标检查一次连续和,如果大于之前统计的最大值,那么就更新最大值,并更新属性1 。对于属性1,随着下标的递增,是非递减的;对于属性2,随着下标的递增,是非递增的。

对于A,按照上述的计算方法(从左到右),其属性1的值如下:

+++

              A串:1  -1  2  2  3  -3  4  -4  5  -5

当前连续和:1    0  2  4  7   4  8    4  9   4

最大连续和:1    1  2  4  7   7  8    8  9   9

+++


对于B,注意此时是从右到左计算

+++

              B串:1  -1  2  2  3  -3  4  -4  5  -5

当前连续和: 9   8  9  7  5   2   5   1  5  -5

最大连续和: 9   9  9  7  5   5   5   5  5  -5

+++


则,此时我们可以从这个集合中选取最大的一个max{1+9, 1+9, 2+7, 4+5, 7+5, 7+5, 8+5, 8+5, 9+(-5)  } = 13

代码如下:

#include<stdio.h>#include<iostream>#include<cmath>#include<algorithm>#define MAX 50005#define MIN -500000009using namespace std;long long a[MAX];long long ltr[MAX]; //left to rightlong long cs,cm;    //current sum, current maxint n;int main(){    int tc;    cin>>tc;    while(tc--)    {        cin>>n;        for(int i=0;i<n;++i)            scanf("%lld",&a[i]);        cm=MIN;        cs=0;        for(int i=0;i<n;++i)        {            cs+=a[i];            if(cs>cm)            {                   cm=cs;                ltr[i]=cm;            }            else                ltr[i]=ltr[i-1];            if(cs<0)             cs=0;        }        cs=0;        cm=MIN;        long long ans=MIN;     //answer        for(int i=n-1;i>0;--i)        {            cs+=a[i];            if(cs>cm)                cm=cs;            if(cs<0)                cs=0;            ans=max(ans,cm+ltr[i-1]);        }        printf("%lld\n",ans);    }}                                 


—————一些思考——————

一开始想能不能够把主串分为子串,然后子串再分子子串那样去做,但是不行,因为主串求的是两个子串的和,子串求的不是两个子子串的和

最关键的是要想到一个是从左到右,一个是从右到左计算

一开始想过要将所有连续负数合并,所有连续正数合并,得到一个新的正负交替的串,但貌似没什么用




******<转载说明>******
转载注明:诚实的偷包贼
原文地址:http://blog.csdn.net/fanfank/article/details/8866563
******<转载说明/>******





原创粉丝点击