这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大。注意:选出的k个子矩阵不能相互重叠。
这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大。注意:选出的k个子矩阵不能相互重叠。
第一行为n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下来n行描述矩阵每行中的每个元素的分值(每个元素的分值的绝对值不超过32767)。
只有一行为k个子矩阵分值之和最大为多少。
dp。
注意到m=1/2,所以两种情况分别算。
m=1时:
f[i][j]表示前i数选出j个子矩阵,sum[i]为前缀和。
f[i][j]=max(f[i-1][j],f[k][j-1]+sum[i]-sum[k]) (0<=k<i)
m=2时:
w[i][j][k]表示左边一列的前i行,右边一列的前j行,分成k个子矩阵。
有三种转移方法:
1.从左边选择一列w[i][j][k]=max(w[i][j][k],w[p][j][k-1]+sum[i][1]-sum[p][1])
2.从右边选择一列w[i][j][k]=max(w[i][j][k],w[i][p][k-1]+sum[j][2]-sum[p][2])
3.如果i=j还可以选择宽为2的矩阵
w[i][j][k]=max(w[i][j][k],w[p][p][k-1]+sum[i][1]-sum[p][1]+sum[i][2]-sum[p][2])
这样就能把所有状态都表示出来了。
#include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #define inf 0x3f3f3f3f using namespace std; int a[105][3],sum[105][3],f[105][105],w[105][105][15],n,m,k; int main() { scanf("%d%d%d",&n,&m,&k); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&a[i][j]); if (!k) { cout<<0<<endl; return 0; } if (m==1) { sum[0][1]=0; for (int i=1;i<=n;i++) sum[i][1]=sum[i-1][1]+a[i][1]; for (int i=0;i<=n;i++) for (int j=1;j<=k;j++) f[i][j]=-inf; for (int i=1;i<=n;i++) for (int j=1;j<=min(i,k);j++) { f[i][j]=f[i-1][j]; for (int p=0;p<i;p++) f[i][j]=max(f[i][j],f[p][j-1]+sum[i][1]-sum[p][1]); } cout<<f[n][k]<<endl; } else { sum[0][1]=sum[0][2]=0; for (int i=1;i<=n;i++) for (int j=1;j<=2;j++) sum[i][j]=sum[i-1][j]+a[i][j]; for (int i=0;i<=n;i++) for (int j=0;j<=n;j++) for (int t=1;t<=k;t++) w[i][j][t]=-inf; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) for (int t=1;t<=min(k,i+j);t++) { w[i][j][t]=max(w[i][j-1][t],w[i-1][j][t]); for (int p=0;p<i;p++) w[i][j][t]=max(w[i][j][t],w[p][j][t-1]+sum[i][1]-sum[p][1]); for (int p=0;p<j;p++) w[i][j][t]=max(w[i][j][t],w[i][p][t-1]+sum[j][2]-sum[p][2]); if (i==j) { for (int p=0;p<i;p++) w[i][j][t]=max(w[i][j][t],w[p][p][t-1]+sum[i][1]-sum[p][1]+sum[i][2]-sum[p][2]); } } cout<<w[n][n][k]<<endl; } return 0; }
感悟:
这道题的关键在于发现m的特殊之处,然后就可以暴力的来做了