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

  • 相关阅读:
    监控里的主码流和子码流是什么意思
    监控硬盘容量计算
    一个能让你了解所有函数调用顺序的Android库
    电工选线
    oracle linux dtrace
    list all of the Oracle 12c hidden undocumented parameters
    Oracle Extended Tracing
    window 驱动开发
    win7 x64 dtrace
    How to Use Dtrace Tracing Ruby Executing
  • 原文地址:https://www.cnblogs.com/qq952693358/p/5827936.html
Copyright © 2011-2022 走看看