動態規劃之--最大子段和/最大子矩陣和

一、最大子段和

最大子段和針對的是一維的情形,採用蠻力法可以在O(n2)O(n^2)的時間複雜度內進行求解。而採用動態規劃的方法則則可以在O(n)O(n)的時間複雜度內進行求解。這裏我們着重介紹動態規劃的方法。
問題描述:
對於長度爲n的數組num[N],求解其最大的連續子段和maxsubSum=n=ijnum[n],0ijnmaxsubSum=\sum\limits_{n=i}^jnum[n],其中有0≤i≤j≤n
解決思路:
規劃規劃矩陣dp[i],dp[i]表示以num[i]作爲結尾元素時的最大字段和。
則有如下狀態轉移方程:
dp[i]=max{dp[i1]+num[i],num[i]} dp[i]=max\{dp[i-1]+num[i],num[i] \}
以上的轉移方程的是顯而易見的,因爲要滿足num[i]num[i]作爲最後一個元素,因此只會存在兩種情況:
①繼續累加dp[i]=dp[i1]+num[i]dp[i]=dp[i-1]+num[i]
②另起爐竈dp[i]=num[i]dp[i]=num[i],顯然只有當dp[i-1]<0時纔會考慮該種情況。

定義完以上的轉移方程之後,最後的答案就是max0indp[i]\max\limits_{0≤i≤n} dp[i],因爲dp[i]包含了以所有元素作爲結尾的所有子段和,所以只需要取裏面的最大值就完事了!

#include <iostream>
#include <climits>
#define N 101
using namespace std;

int buf[N];

int max(int a,int b){return a>b?a:b;}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>buf[i];

    int maxsum=INT_MIN;
    int cursum=0;
    for(int i=0;i<n;i++){
        cursum = max(buf[i],buf[i]+cursum);//cursum[i]指的是以當前元素作爲結尾時最大子段和,
                                           //因此要麼另起爐竈,要麼繼續累積。
        maxsum = max(cursum,maxsum);
    }
    cout<<maxsum<<endl;
    return 0;
}
/**
10
1 3 5 7 -2 9 -1  -3 -4 -5
*/

二、最大子矩陣和

最大子矩陣和是二維的情形,採用蠻力法可以在O(n4)O(n^4)的時間複雜度內解決,可想而知到n>100的時候就快扛不住了,而採用動態規劃算法則可以在O(n3)O(n^3)的時間複雜度內解決該問題,直接降低了一個數量級。有了以上的最大子段和的算法作爲鋪墊之後,我們求解最大子矩陣和就方便多了。
問題描述:
對於尺寸爲N×NN×N的矩陣num[N][N],求解其最大的子矩陣和。
以下圖爲例,矩陣num的最大子矩陣如下圖紅框所示::
在這裏插入圖片描述
解決思路
前面已經鋪墊了這個問題其實和求解最大子段和有關,那麼現在的問題就在於怎麼把問題從求最短子段和轉換爲求最大子矩陣。問題的關鍵就在於求最大子矩陣問題本質上就是求n*n次最大子段和的問題,試想一下,我們需要對於一維數組b[N]求解12nn\frac{1}{2}*n*n次的最大子段和,而每一次存儲的是第i行到第j行中對應的每一列的累加和。下面畫圖爲例:
在這裏插入圖片描述
因此現在需要做的就是把所有的i~j的情況遍歷一遍,記錄所有情況下的最大子段和之後,便可以得到最終的最大子矩陣和,方法也顯而易見,所有的子矩陣都被我們求過了。所以只需要找到其中的最大值即可。

#include <iostream>
#include <climits>
#define N 100
using namespace std;
int buf[N][N];
int tmp[N][N];//輔助矩陣,其中tmp[i][j]存儲的是前i行的第j列的累加之和
int b[N];
int max(int a,int b){return a>b?a:b;}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>buf[i][j];
        }
    }
    for(int i=0;i<n;i++)//初始化第一行
        tmp[0][i]=buf[0][i];
    for(int i=1;i<n;i++){
        for(int k=0;k<n;k++){
            tmp[i][k]=tmp[i-1][k]+buf[i][k];//累加上一行的值求和
        }
    }
    int maxsum = INT_MIN;
    for(int i=0;i<n;i++){
        for(int j=i;j<n;j++){
            int cursum=0;
            for(int k=0;k<n;k++){
                if(i==0){
                    b[k]=tmp[j][k];
                }
                else{
                    b[k]=tmp[j][k]-tmp[i-1][k];//得到第i行到第j行的累加之和
                }
                cursum=max(b[k],b[k]+cursum);
                maxsum=max(maxsum,cursum);
            }
        }
    }
    cout<<maxsum<<endl;
    return 0;
}

參考博客:https://blog.csdn.net/Double2hao/article/details/51727420

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章