zoukankan      html  css  js  c++  java
  • 【最长连续子序列和】三种复杂度的算法

    题目:

    ★实验任务:为了打破进了实验室就嫁不出去的诅咒,六一儿童节这天集训队特地举办了一场相亲大会,来自各个学院的n个姑娘在实验室内站成一排。每个姑娘有自己的颜值ai。单身狗们决定邀请颜值之和最高的k个(k要大于0)位置相邻的姑娘一起晚上的狼人杀。
    ★数据输入:输入第一行为一个数n(1<=n<=100000)表示姑娘个数。接下来一行有n个整数ai(-1000<=ai<=1000)表示第i个姑娘的颜值。
    ★数据输出:输出一行为最大连续子串和。
    输入示例:5 6 -1 5 4 -7
    输出示例:14
    输入示例:7 0 6 -1 1 -6 7 -5
    输出示例:7

    三种做法:

    1.O(n^2)

    //
    //  main.cpp
    //  MaxSubSequence
    //
    //  Created by wasdns on 16/8/31.
    //  Copyright © 2016年 wasdns. All rights reserved.
    //
    
    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <string>
    #include <algorithm>
    using namespace std;
    
    int number[1000005];
    
    int MaxSubSequence(int n) //复杂度为O(n^2)
    {
        int MaxSum = 0;
        
        for(int i = 0; i < n; i++)
        {
            int Cal = 0;
            
            for(int j = i; j < n; j++)
            {
                Cal += number[j];
                
                if(Cal > MaxSum) //利用先前计算的结果进行比较
                {
                    MaxSum = Cal;
                }
            }
        }
        
        return MaxSum;
    };
    
    int main()
    {
        int n, i;
        cin >> n;
        
        for(i = 0; i < n; i++)
        {
            cin >> number[i];
        }
        
        int MaxSum = MaxSubSequence(n);
        
        cout << MaxSum << endl;
        
        return 0;
    }
    
    

    2.O(nlogn)

    //
    //  main.cpp
    //  MaxSubSequence_2
    //
    //  Created by wasdns on 16/8/31.
    //  Copyright © 2016年 wasdns. All rights reserved.
    //
    
    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    
    int number[1000005];
    
    int MaxCalculator(int left, int right);
    
    int MaxSubSequence(int left, int right)
    {
        return MaxCalculator(left, right);
    }
    
    int CalMax(int a, int b, int c)
    {
        if(a > b)
        {
            if(a > c) return a;
            else return c;
        }
        else
        {
            if(b > c)return b;
            else return c;
        }
    }
    
    int MaxCalculator(int left, int right)
    {
        if(left == right)
        {
            if(number[left] > 0) return number[left];
            else return 0;
        }
        
        int MaxLeftSum = 0;
        int MaxRightSum = 0;
        int middle;
        
        //cout << left << " " << right << endl;
        middle = (left + right)/2;
        //cout << middle << endl;
        
        MaxLeftSum = MaxCalculator(left, middle);
        MaxRightSum = MaxCalculator(middle + 1, right); //没有+1:导致 0 1 循环
        
        int MLASum = 0; //MaxLeftAreaSum
        int MRASum = 0; //MaxRightAreaSum
        
        int MSum = 0;
        for(int i = middle; i >= left; i--)
        {
            MSum += number[i];
            if(MSum > MLASum) MLASum = MSum;
        }
        
        MSum = 0;
        for(int i = middle + 1; i <= right; i++)
        {
            MSum += number[i];
            if(MSum > MRASum) MRASum = MSum;
        }
        
        return CalMax(MaxLeftSum, MaxRightSum, MLASum + MRASum);
    }
    
    int main()
    {
        int n, i;
        
        cin >> n;
        
        for(i = 0; i < n; i++)
        {
            cin >> number[i];
        }
        
        cout << MaxSubSequence(0, n-1) << endl;
        
        return 0;
    }
    
    

    3.O(nlogn):常用的动态规划

    //
    //  main.cpp
    //  MaxSubSequence_3
    //
    //  Created by wasdns on 16/8/31.
    //  Copyright © 2016年 wasdns. All rights reserved.
    //
    
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string>
    #include <string.h>
    using namespace std;
    
    int Number[100005];
    int b[100005]; //含当前位置元素的最大子序列和 不断的更新
    
    int MSS(int n) //状态转移方程:b[i] = MAX{b[i-1] + a[i], a[i]};
    {
        memset(b, 0, sizeof(b));
        int i;
        int sum = Number[0]; //sum 初始化为 Number[0]
        
        b[0] = Number[0];
        
        for(i = 1; i < n; i++)
        {
            if(b[i-1] + Number[i] > Number[i])
            {
                b[i] = b[i-1] + Number[i];
            }
            else b[i] = Number[i];
            
            //cout << "b[i] = " << b[i] << endl;
            
            if(sum < b[i]) sum = b[i];
            
            //cout << "sum = " << sum << endl;
        }
        
        return sum;
    }
    
    int main()
    {
        int n;
        
        cin >> n;
        
        for(int i = 0; i < n; i++)
        {
            cin >> Number[i];
        }
        
        int MaxSequenceSum = MSS(n);
        
        cout << MaxSequenceSum << endl;
        
        return 0;
    }
    
    

    小结

    给出的第一种解决代码,首先复杂度相比使用三个for循环的O(N^3)下降了很多,但是仍然达不到要求。利用的是 之前计算的结果,从当前位置一个一个加过去。

    实现算法:

    int MaxSubSequence(int n) //复杂度为O(n^2)
    {
        int MaxSum = 0;
        
        for(int i = 0; i < n; i++)
        {
            int Cal = 0;
            
            for(int j = i; j < n; j++)
            {
                Cal += number[j];
                
                if(Cal > MaxSum) //利用先前计算的结果进行比较
                {
                    MaxSum = Cal;
                }
            }
        }
        
        return MaxSum;
    };
    

    第一种易于理解,但是算法复杂度过高。因此为了适应题目的要求,提到了第二种的解决方法。

    算法实现:最大子序列和,要么出现在 1)left 和 middle 之间,要么出现2)在 middle 和 right 之间,还有 3)middle在这个子序列中。一共三种情况,分别计算出三种情况的最大子序列的大小,取最大值。

    前面两种方法,可以使用 递归分治 的思想:更新middle -> 计算上面三种情况的子序列大小 -> 利用递归 -> 更新middle ···
    当最后 left 和 middle 重合的时候(或者 middle 和 right 重合的时候),判断 number[left] 是否大于0,大于0返回number[left],小于0返回0。

    当利用递归 计算完成1)和2)的值之后,转而计算3)的值:左边从middle开始遍历,找到left;右边从middle+1开始遍历,找到right;两边的值相加即可求得3)。

    实现代码:

    int CalMax(int a, int b, int c)
    {
        if(a > b)
        {
            if(a > c) return a;
            else return c;
        }
        else
        {
            if(b > c)return b;
            else return c;
        }
    }
    
    int MaxCalculator(int left, int right)
    {
        if(left == right) //递归终止的条件
        {
            if(number[left] > 0) return number[left];
            else return 0;
        }
        
        int MaxLeftSum = 0;
        int MaxRightSum = 0;
        int middle;
        
        //cout << left << " " << right << endl;
        middle = (left + right)/2;
        //cout << middle << endl;
        
        MaxLeftSum = MaxCalculator(left, middle);
        MaxRightSum = MaxCalculator(middle + 1, right); //注意!没有+1:导致 0 1 循环
        
        int MLASum = 0; //MaxLeftAreaSum
        int MRASum = 0; //MaxRightAreaSum
        
        int MSum = 0;
        for(int i = middle; i >= left; i--) //从middle左边开始遍历
        {
            MSum += number[i];
            if(MSum > MLASum) MLASum = MSum;
        }
        
        MSum = 0;
        for(int i = middle + 1; i <= right; i++) //从middle+1右边开始遍历
        {
            MSum += number[i];
            if(MSum > MRASum) MRASum = MSum;
        }
        
        return CalMax(MaxLeftSum, MaxRightSum, MLASum + MRASum); //取三种情况的最大值
    }
    

    第三种,即最常见的动态规划问题了。动态规划是一种利用之前计算结果的算法,我们这里使用了b[i]数组来存储:b[i]代表的意思是,经过number[i]的最大子序列。
    如果b[i-1]+number[i]大于number[i],那么加到此处的最大子序列b[i]更新为b[i-1]+number[i];否则,将b[i]更新为number[i]重新开始。记录整个过程中的最大子序列和sum。状态转移方程:b[i] = MAX{b[i-1]+number[i], number[i]}
    注意:sum需要初始化为Number[0]。

    实现代码:

    int MSS(int n) //状态转移方程:b[i] = MAX{b[i-1] + a[i], a[i]};
    {
        memset(b, 0, sizeof(b));
        int i;
        int sum = Number[0]; //sum 初始化为 Number[0]
        
        b[0] = Number[0];
        
        for(i = 1; i < n; i++)
        {
            if(b[i-1] + Number[i] > Number[i])
            {
                b[i] = b[i-1] + Number[i];
            }
            else b[i] = Number[i];
            
            //cout << "b[i] = " << b[i] << endl;
            
            if(sum < b[i]) sum = b[i];
            
            //cout << "sum = " << sum << endl;
        }
        
        return sum;
    }
    

    2016/8/31

  • 相关阅读:
    :Spring + axis2 开发 webservice
    log file parallel write 和 log buffer space p1 p2 p3
    log file sync p1 p2 p3
    :Apache FTPClient操作“卡死”问题的分析和解决
    :Apache FTPClient操作“卡死”问题的分析和解决
    org.apache.catalina.LifecycleException: Failed to start component
    一个简单的Tk界面(可以录入和查询)
    函数调用子函数,注意子函数的位置
    Perl 采集磁盘信息
    Perl 使用Frame(放置其他控件的地方)
  • 原文地址:https://www.cnblogs.com/qq952693358/p/5827936.html
Copyright © 2011-2022 走看看