[Luogu 2331] [SCOI2005]最大子矩阵
[Luogu 2331] [SCOI2005]最大子矩阵
题目描述
这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大。注意:选出的k个子矩阵不能相互重叠。
输入输出格式
输入格式:
第一行为n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下来n行描述矩阵每行中的每个元素的分值(每个元素的分值的绝对值不超过32767)。
输出格式:
只有一行为k个子矩阵分值之和最大为多少。
输入输出样例
输入样例#1:
3 2 2
1 -3
2 3
-2 3
输出样例#1:
9
又是一道DP题,由于看题太过迅速,竟然没有看清m<=2!!!(好气哦qaq)
题解:
既然在知道了m<=2的条件下,那么应该是可以想到,对m=1和m=2分别考虑
(1)m=1的情况其实就是一个求k个最大字段和
那么可以用一个二维数组f[i][j]表示到了第i位选取了j个字段
那么转移就是分为不选第i位和选第i位的情况
f[i][j]=f[i-1][j]
f[i][j]=max(f[i][j],f[l-1][j-1]+sum[i]-sum[l])(1<=l<=i)
(2)m=2的情况就是一个三维DP
f[i][j][k]表示第一列选到第i行,第二列选到第j行,一共选了k个矩形
那么转移分为四种第i和第j行都不选,选第i行,选第j行,同时选第i和第j行(前提i=j)
那么具体的转移方程请见程序
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=110; 4 int n,m,k,sum,ans; 5 int a[N][3],sum1[N],sum2[N],f[N][N][12],g[N][12]; 6 int main(){ 7 scanf("%d%d%d",&n,&m,&k); 8 for (int i=1;i<=n;++i) 9 for (int j=1;j<=m;++j) 10 scanf("%d",&a[i][j]); 11 for (int i=1;i<=n;++i) sum1[i]=sum1[i-1]+a[i][1]; 12 if (m==2) for (int i=1;i<=n;++i) sum2[i]=sum2[i-1]+a[i][2]; 13 if (m==1){ 14 for (int i=1;i<=k;++i) g[0][i]=0; 15 for (int i=1;i<=n;++i) 16 for (int j=1;j<=k;++j){ 17 g[i][j]=g[i-1][j]; 18 for (int l=1;l<=i;++l) g[i][j]=max(g[i][j],g[l-1][j-1]+sum1[i]-sum1[l-1]); 19 } 20 printf("%d",g[n][k]); return 0; 21 } 22 for (int i=0;i<=k;++i) f[0][0][i]=0; 23 for (int i=1;i<=n;++i) 24 for (int j=1;j<=n;++j) 25 for (int l=1;l<=k;++l){ 26 f[i][j][l]=max(f[i-1][j][l],f[i][j-1][l]); 27 for (int h=1;h<=i;++h) 28 f[i][j][l]=max(f[i][j][l],f[h-1][j][l-1]+sum1[i]-sum1[h-1]); 29 for (int h=1;h<=j;++h) 30 f[i][j][l]=max(f[i][j][l],f[i][h-1][l-1]+sum2[j]-sum2[h-1]); 31 if (i==j) for (int h=1;h<=i;++h) f[i][j][l]=max(f[i][j][l],f[h-1][h-1][l-1]+sum1[i]+sum2[j]-sum1[h-1]-sum2[h-1]); 32 } 33 printf("%d",f[n][n][k]); 34 }