最大连续子序列的和

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hqh131360239/article/details/82836332

1、单调递增子序列(dp)。2、最长公共子序列(dp)。3、最大连续子序列的和(dp)。

上述问题,都是经典的dp算法实例,但是针对问题的变形,就很难再用dp来处理了,反正我不会,暴力出奇迹,可以用分治的方法同样解决问题1和3。

①最大连续子序列的和;最大连续子序列的和,要求个数必须大于1;最大连续子序列的和的绝对值,要求可以改变k个元素的正负号。

思路:前两种,我以前总结过。第三种,这几天刚遇到,刚开始毫无思路,最后看到分治的思路,暴力了一波。分治解法:先找出mid,左右找(left,mid)和(mid+1,right)的最大值leftSum和rightSum;再找横跨mid的最大值sum,最后比较leftSum、rightSum、sum三个值的大小,返回最大值。左右找的同时,递归的k值不同,左右改变的正负号之后为k。

#include <iostream>
#include <algorithm>
#include <math.h>
using namespace std;
int n,k;
int a[1000005];
int Max(int l,int r,int k){
    int sum=0;
    if(l==r){
        if(a[l]>0) sum=a[l];    //可以判断k的值
        else if(k>0){
            sum=-a[l];
            k--;
        }
        else sum=0;
    }else{
        int mid=(l+r)/2;
        for(int kk=0;kk<k;kk++){
            int leftsum=Max(l,mid,kk);
            int rightsum=Max(mid+1,r,k-kk);
            int lefts=0,s1 = 0;

            int judge=kk;
            for(int i=mid ;i>=l;i--){
                if(a[i]<0&&judge>0){
                    lefts-=a[i];
                    judge--;
                }else
                    lefts+=a[i];
                if(lefts>s1){
                    s1=lefts;
                }
            }
            int rights=0,s2=0;
            for(int j=mid+1;j<=r;j++){
                if(a[j]<0&&(k-judge)>0){
                    rights-=a[j];
                    judge--;
                }else
                    rights+=a[j];
                if(rights>s2){
                    s2=rights;
                }
            }
            sum=s1+s2;
            if(max(leftsum,rightsum)>sum)
            sum=max(leftsum, rightsum);
        }
    }
    return sum;
}
int Min(int l,int r,int k){
    int sum=0;
    if(l==r){
        if(a[l]<0) sum=a[l];    //可以判断k的值
        else if(k>0){
            sum=-a[l];
            k--;
        }
        else sum=0;
    }else{
        int mid=(l+r)/2;
        for(int kk=0;kk<k;kk++){
            int leftsum=Min(l,mid,kk);
            int rightsum=Min(mid+1,r,k-kk);
            int lefts=0,s1 = 0;

            int judge=kk;
            for(int i=mid ;i>=l;i--){
                if(a[i]>0&&judge>0){
                    lefts-=a[i];
                    judge--;
                }else
                    lefts+=a[i];
                if(lefts<s1){
                    s1=lefts;
                }
            }
            int rights=0,s2=0;
            for(int j=mid+1;j<=r;j++){
                if(a[j]>0&&(k-judge)>0){
                    rights-=a[j];
                    judge--;
                }else
                    rights+=a[j];
                if(rights<s2){
                    s2=rights;
                }
            }
            sum=s1+s2;
            if(min(leftsum,rightsum)<sum)
            sum=min(leftsum,rightsum);
        }
    }
    return sum;
}
int main()
{
    while(cin>>n){
        for(int i=0;i<n;i++){
            cin>>a[i];
        }
        cin>>k;
        cout<<max(abs(Min(0,n-1,k)),Max(0,n-1,k))<<endl;
    }
}
/*
8
1 -2 3 10 -4 7 2 -5
3
8
1 -2 -3 -10 4 -7 2 -5
3
*/

参考资料:

https://blog.csdn.net/u014257192/article/details/52971343

②那么我就想了,如果求单调递增子序列,也可以改变k个值得大小,那么求最长的长度?能用同样的方法吗?

感觉没意义了,只要统计两个递增数字自己的最大间隔就行了,然后与k对比。在原基础要么加上最大间隔,要么加上k。

如:7、6、5、8、4、3、9、2、1

但是针对连续递增子序列的最大长度,注意这里就不能用到dp了,起始位置不同决定长度的不同,可以暴力起始位置,时间复杂度为O(N^2),同理也可以用分治。好处是对于修改k个值后,判断最长长度。

参考资料:

https://blog.csdn.net/blue_hpu/article/details/63320016

③三角行的判断(任意两边之和大于第三边或最小两边之和大于第三边)

思路:方法一和方法二都是用到了简单的暴力方法,方法三在方法二的基础上增加了剪枝。定两条最小边,如果找不到第三条边,那么整个程序就可以终止了。因为是有序的序列,在小值得时候不满足了,那么往后值越来越大就更不可能满足条件了。找到第一个满足条件的k,那么后面的n-k个也满足。

#include <iostream>
using namespace std;
int main()
{
    int t,n,sum;
    int a[2001];  //存储木棒长度
    cin>>t;       //t组数据
    while(t--){
        cin>>n;   //每组n个木棒
        sum=0;
        for(int i=0;i<n;i++){
            cin>>a[i];
        }
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                for(int k=j+1;k<n;k++){
                    //任意两边之和大于第三边
                    if(a[i]+a[j]>a[k]&&a[i]+a[k]>a[j]&&a[j]+a[k]>a[i]){
                        sum++;
                    }
                }
            }
        }
        cout<<sum<<endl;
    }
    return 0;
}
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    int t,n,sum;
    int a[2001];  //存储木棒长度
    cin>>t;       //t组数据
    while(t--){
        cin>>n;   //每组n个木棒
        sum=0;
        for(int i=0;i<n;i++){
            cin>>a[i];
        }
        sort(a,a+n); //排序
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                for(int k=j+1;k<n;k++){
                    //最小两边之和大于第三边
                    if(a[i]+a[j]>a[k]){
                        sum++;
                    }else{
                        break;
                    }
                }
            }
        }
        cout<<sum<<endl;
    }
    return 0;
}
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    int t,n,sum,flag;
    int a[2001];  //存储木棒长度
    cin>>t;       //t组数据
    while(t--){
        cin>>n;   //每组n个木棒
        sum=0;
        flag=0;
        for(int i=0;i<n;i++){
            cin>>a[i];
        }
        sort(a,a+n); //排序
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                //排序后,强力剪枝
                int k;     //选择第一个符合条件的k值
                for(k=j+1;k<n;k++)
                    if(a[i]+a[j]>a[k]) break;
                if(k==n){  //一旦出现找不到的情况,数据越来越大,后面的更没戏
                    flag=1;break;
                }else{
                    sum+=(n-k);
                }
            }
            if(flag==1) break;
        }
        cout<<sum<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/hqh131360239/article/details/82836332