• 【算法学习笔记】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
    ";
    }
    View Code

    补充链接:

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

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

    
    
  • 相关阅读:
    robots.txt
    procdump和mimikatz工具配合破解windows账户口令
    通过vbs脚本控制方向盘按键
    批处理删除文件或文件夹代码
    彩色线条雨特效html代码
    secureCRT
    chrome 更新flash插件
    python命令行下安装redis客户端
    FastJson使用
    Redis 学习(二)
  • 原文地址:https://www.cnblogs.com/yuchenlin/p/sjtu_oj_1006.html
Copyright © 2020-2023  润新知