POJ - 1050 To the Max(最大连续子段和,线性dp)
題目鏈接:點擊查看
題目大意:給出一個n*n的矩陣,每個點都有一個權值,現在要從中選取一個子矩陣要求權值和最大,問這個最大權值和是多少
題目分析:因為是要求子矩陣的權值和最大的問題,我們可以直接維護一個二維前綴和,然后n^4暴力求解即可,簡單暴力不多說了,著重說一下另一個思想
上面的4層for分別枚舉的是行起點,行終點,列起點,列終點,我們可以通過線性dp將列起點和列終點這兩層for優化成一層for,具體的實現就是求一個最大連續子段和,我們可以將行起點到行終點中的所有行對應的列累加到一個一維數組中,然后求一下這個數組的最大連續子段和,就是當前枚舉的行之中最大的答案了,實時維護一下最大值就好了
就是突然感覺,最大連續子段和的轉移方程一下子就能自己寫出來了,感覺和前兩天做的那個樹形dp很像,每個點都有兩種選擇,一種選擇是接在前面的后面,另一種選擇是自己重新當新的開頭,當然如果前置狀態對當前的貢獻為正,肯定是接在前面的后面更優,但如果前置的狀態為負,當然還是自己獨成一個開頭更優,實現很簡單,有注釋看起來應該不難
還有一種實現方法,更加廣義一點,我們可以上升為求子段長度不小于L的最大連續子段和,這個時候我們就需要借助前綴和來實現了,轉移方程就是dp[i]=sum[i]-min(sum[j]),j=[0,i-L],乍一看,時間復雜度是n*n級別的,其實不然,后面求最小值的那個部分還可以繼續優化,我們不妨用一個變量mmin直接維護一下前i-L中的最小值,這樣就能將時間復雜度優化至O(n)了,具體實現也很簡單:
for(int i=L;i<=n;i++)
{
? ? mmin=min(mmin,sum[i-L]);
? ? ans=max(ans,sum[i]-mmin);
}
代碼:
1:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=110;int n;int maze[N][N];//維護最初的矩陣int sum[N];//維護的一維矩陣int solve()//最大連續子段和 {int ans=-inf;//答案int temp=0;//當前的子段和for(int i=1;i<=n;i++){if(temp<0)//不要前面的 temp=sum[i];else//要前面的 temp+=sum[i];ans=max(ans,temp);//實時更新答案}return ans; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);while(scanf("%d",&n)!=EOF){for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&maze[i][j]);int ans=-inf;for(int i=1;i<=n;i++)//行起點 {memset(sum,0,sizeof(sum));for(int j=i;j<=n;j++)//行終點 {for(int k=1;k<=n;k++)sum[k]+=maze[j][k];ans=max(ans,solve());}}printf("%d\n",ans);}return 0; }2:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=110;int n;int maze[N][N];int sum[N];int summ[N];int solve() {int ans=-inf;int temp=0;for(int i=1;i<=n;i++)//ssum維護sum的前綴和summ[i]=summ[i-1]+sum[i];int mmin=inf;for(int i=0;i<=n;i++){mmin=min(mmin,summ[i]);ans=max(ans,summ[i]-mmin);}return ans; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);while(scanf("%d",&n)!=EOF){for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&maze[i][j]);int ans=-inf;for(int i=1;i<=n;i++)//行起點 {memset(sum,0,sizeof(sum));for(int j=i;j<=n;j++)//行終點 {for(int k=1;k<=n;k++)sum[k]+=maze[j][k];ans=max(ans,solve());}}printf("%d\n",ans);}return 0; }?
總結
以上是生活随笔為你收集整理的POJ - 1050 To the Max(最大连续子段和,线性dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CH - 0304 IncDec Seq
- 下一篇: POJ - 2018 Best Cow