http://ybt.ssoier.cn:8088/problem_show.php?pid=1282(题目传送)
虽然已知是DP,但第一眼看挺蒙的,想了想后设了个a[i][j][k][l]表示长(坐标)为i~j,宽(坐标)为k~l的矩阵,但根本找不到状态转移方程啊。后借鉴题解(https://www.cnblogs.com/GodA/p/5237061.html)后领悟到的另一种方法:
任何问题都有它的简化,看到二维,没办法时我们可以考虑一下一维:求一维数组的一个最大连续段,我们可以设b[i]为从前i个数的最大连续段(a[i]为一开始存入的输入数据),b[i]无非就有两种情况:以它结尾的最大连续段;或以它开头另开一个最大连续段(当b[i-1]小于0时b[i-1]+a[i]反而比a[i]小,因此不如新开一个最大连续段)。
1 int MaxSubArray(int a[],int n) 2 { 3 int i,b = 0,sum = 0; 4 for(i = 0;i < n;i++) 5 { 6 if(b>0) // 若a[i]+b[i-1]会减小 7 b += a[i]; // 则以a[i]为首另起一个子段 8 else 9 b = a[i]; 10 if(b > sum) 11 sum = b; 12 } 13 return sum; 14 }
想完一维,跟二维有什么关系?当然有了,我们把二维“拍”成一维,不就行了吗?
我们假设所求N*N的矩阵的最大子矩阵是从i列到j列,q行到p行,如下图所示(假设下标从1开始)
a[1][1] a[1][2] ······ a[1][i] ······ a[1][j] ······ a[1][n]
a[2][1] a[2][2] ······ a[2][i] ······ a[2][j] ······ a[2][n]
······
a[q][1] a[q][2] ······ a[q][i] ······ a[q][j] ······ a[q][n]
······
a[p][1] a[p][2] ······ a[p][i] ······ a[p][j] ······ a[p][n]
······
a[n][1] a[n][2] ······ a[n][i] ······ a[n][j] ······ a[n][n]
最大子矩阵就是图示红色部分,如果把最大子矩阵同列的加起来,我们可以得到一个一维数组{a[q][i]+······+a[p][i] , ······ ,a[q][j]+······+a[p][j]} ,现在我们可以看出,这其实就是一个一维数组的最大子段问题:
分别将各个高的矩阵压缩成一个一维数组并求它的最大连续段,则在二维的体现就是一个相应高的最大连续矩阵,取所有相应最大连续矩阵的最大值,就是答案(还好题目只要求最大值,不要求给出最大矩阵是什么)
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 int dp[101][101],arr[101]; 6 int maxsub(int a[],int n) 7 { 8 int max=0,b=0; 9 for(int i=0;i<=n;i++) 10 { 11 if(b>0) 12 b+=a[i]; 13 else 14 b=a[i]; 15 if(b>max) max=b; 16 } 17 return max; 18 } 19 int main() 20 { 21 int n,maxx=0; 22 cin>>n; 23 for(int i=1;i<=n;i++) 24 for(int j=1;j<=n;j++) 25 cin>>dp[i][j]; 26 for(int i=1;i<=n;i++) 27 { 28 memset(arr,0,sizeof(arr)); 29 for(int j=i;j<=n;j++) 30 { 31 for(int k=1;k<=n;k++) 32 arr[k]+=dp[j][k]; 33 int m=maxsub(arr,n); 34 if(maxx<m) 35 maxx=m; 36 } 37 } 38 cout<<maxx; 39 return 0; 40 }
最后总结一下:1.对问题的简化(二维转一维,模拟转整体充分条件(长方体),无序转有序等)十分重要!
2.线型动态规划多以结尾为状态。
原文地址:https://www.cnblogs.com/InductiveSorting-QYF/p/10503140.html