思路:
因为m最大只有2,所以可以先算m==1的时候,就相当于求最大m子串和,状态转移方程为 :
dp[i][k][0]=max(dp[i-1][k][0],dp[i-1][1][k]);
dp[i][k][1]=max(dp[i-1][k][1],max(dp[i-1][k-1][1],dp[i-1][k-1][0]))+num[i];
然后m==2的时候,用dp[i][j][k]来表示,第一列为1-i,第二列为1-j,k为选的k个矩形。 sum[n][1]表示第一列的前缀和,sum[n][2]表示第二列的前缀和。
计算的时候,我们可以当做是分别计算第一列和第二列的最大m子串和,然后再把他们两列进行整合,因此可以分为四种情况:
计算第一列的时候:
dp[i][j][k]=max(dp[i][j][k],dp[t][j][k-1]+sum[i][1]-sum[t][1]);
计算第二列的时候:
dp[i][j][k]=max(dp[i][j][k],dp[i][t][k-1]+sum[j][2]-sum[t][2]);
当i==j的时候,会有他们两列的子串重合为一个矩形的可能:
dp[i][j][k]=max(dp[i][j][k],dp[t][t][k-1]+sum[i][1]-sum[t][1]+sum[j][2]-sum[t][2]);
什么都不选的情况:
dp[i][j][k]=max(dp[i-1][j][k],dp[i][j-1][k]);
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <string> #include <stack> #include <queue> #include <cmath> #define ll long long #define pi 3.1415927 #define inf 0x3f3f3f3f #define mod 1000000007 using namespace std; int sum[105][5],dp[105][105][12],num[105][5]; int main () { int T,n,m,i,t,j,k,p; cin>>n>>m>>k; for(i=1;i<=n;++i) for(j=1;j<=m;++j){ cin>>num[i][j]; sum[i][j]=sum[i-1][j]+num[i][j]; //计算前缀和 } if(m==1) { for(i=1;i<=n;++i) for(int kk=1;kk<=k;++kk){//m==1的时候,相当于求最大m子串和 dp[i][0][kk]=max(dp[i-1][0][kk],dp[i-1][1][kk]); dp[i][1][kk]=max(dp[i-1][1][kk],max(dp[i-1][1][kk-1],dp[i-1][0][kk-1]))+num[i][1]; } p=max(dp[n][1][k],dp[n][0][k]); cout<<p<<endl; } else { for(i=1;i<=n;++i) for(j=1;j<=n;++j) for(int kk=1;kk<=k;++kk) { dp[i][j][kk]=max(dp[i-1][j][kk],dp[i][j-1][kk]); //什么都不选的情况 for(t=0;t<i;++t) dp[i][j][kk]=max(dp[i][j][kk],dp[t][j][kk-1]+sum[i][1]-sum[t][1]); //在第一列选区间 for(t=0;t<j;++t) dp[i][j][kk]=max(dp[i][j][kk],dp[i][t][kk-1]+sum[j][2]-sum[t][2]); //在第二列选区间 if(i==j) for(t=0;t<j;++t) //当i==j的时候,有可能出现区间相同的情况 dp[i][j][kk]=max(dp[i][j][kk],dp[t][t][kk-1]+sum[i][1]-sum[t][1]+sum[j][2]-sum[t][2]); } cout<<dp[n][n][k]<<endl; } return 0; }