【算法学习笔记】33.在线算法 SJTU OJ 1006 求和游戏

Description

石柱上有一排石头键盘,每个键上有一个整数。请你在键盘上选择两个键,使这两个键及其之间的键上的数字和最大。如果这个最大的和不为正,则输出“Game Over"。

Input Format

第1行:键的个数n。

第2..n+1行:键上的数字整数 ai

100ai100

对于70%的数据,2n1,000

对于100%的数据,2n1,000,000

Output Format

一行,最大和或者”Game Over"。

Sample Input

5
3
-5
7
-2
8

Sample Output

13

Sample Input

3
-6
-9
-10

Sample Output

Game Over

这个题和之前关于求最大子列和的文章非常相似,但是又有不同,因为这是一个应用题...它要求至少要有两个键才行,所以最小的子序列也要保证长度大于等于2.

那么在这种情况下,我们原来的在线算法就不能直接使用了.

这里有两个新的在线算法来完成这个问题, 一个是专门为这个问题而改良的,一个是全新的思路可以解决原问题和这个新问题.

新在线算法1:
  核心思想依然是:遇到了负数可以进行重置当前和. 但是在驱动的过程中要考虑到始终保持最小长度为2,所以要不断的错位来完成这件事情.
再处理的过程中,我们所需要决策的就是i一定是要续接上的,至于头部是继续上一次的,还是它的前一个位置 只需比较一下大小即可.
如果n[i-1]比当前的和大, 那么就从它开始续接不就好啦~ 如果当前和更大, 那就要继续下去
代码如下:
int main(){
    int i,n,max,temp;
    cin>>n;
    int num[n];
    for (i=0;i<n;++i) 
        cin>>num[i];
    max = temp = num[0]+num[1];

    for (i=2;i<n;++i) { //对于每个i 都有两种结果 要么把它加进来 要么把当前片段重置为从i-1开始

        temp = temp > num[i-1] ? 
            temp     + num[i] : 
            num[i-1] + num[i];
        //如果temp > num[i] 那么续接上num[i]肯定可以更大
        //如果temp < num[i] 那么就可以从n[i-1]重置续接 那一定比以前大
        if (max<temp) 
            max = temp;
    }

    if (max>0) cout<<max;
    else cout<<"Game Over";

    return 0;
}
View Code

 

第二种方法是这样的思想:

1.一个连续子序列的和 =从第一个数到这个序列的尾端的和 - 从第一个数到这个序列尾端的和.

2.要想使等号左边最大,在每次计算中只要让 最后一项最小即可to

所以 我们要维护 smallest 然后所有的curtotal - smallest里找到最大值即可; 

但是这里就涉及到一个非常关键的问题: 先维护最小值还是先更新当前连续子序列的和?

如果先维护最小值, 那么在

if (result < total - smallest)
  result = total - smallest;

中的smallest就是指 前i-1个数组成的序列中连续子列和最小值

然后用total去减,最少得到的就是只有一个元素(如果smallest的尾部是i-1的话) 也就是经典问题

然而我们要求的是smallest必须指 前i-2个数组成的序列中子列和最小值, 这样减法之后至少有两个元素. 也就是应用题

要达成这个目的,我们需要用到一个技巧就是 后置更新法, 把smallest的更新 放在上面这句话的后边 那么它在被调用的时候,实际上还是到i-2的为止的最小值

代码如下:

int main()
{
    int num(0);
    cin >> num;
    int result(0);
    int total(0), smallest(32785);
//smalest 表示的是从第一个数开始 的 连续子序列和最小的时候的那个和 也就是以某一个数为尾端
//total 表示的是从 第一个数 开始 到 现在 为止的所有数的和 也就是以现在为尾端
//两者相减就是 从smallest结尾 到现在为止的子序列和 然后用result来维护最大值
    for (int i = 0, temp; i < num; ++i)
    {
        cin >> temp;
        total += temp;
        //result =  max(result, total-smallest)
        //在此处由于smallest维护的是到前2个数为止的
        //那么相减的结果必然会至少包含前一个数
        if (result < total - smallest)  
            result = total - smallest;
        //smallest = min(smallest, total-temp )
        //此处维护的smallest是从第一个数到当前数的前2个数的
        //因为i饿total - temp 其实是到i-1为止的total
        if (smallest > total - temp) 
            smallest = total - temp;
    }
    if (result > 0) cout << result << endl;
    else cout << "Game Over\n";
}
View Code

补充链接:

http://blog.csdn.net/hcbbt/article/details/10454947 六种方法解决连续子序列最大和

http://www.cnblogs.com/txd0u/p/3353355.html 1006题解

 


posted @ 2015-04-23 01:56  雨尘之林  阅读(756)  评论(0编辑  收藏  举报