BZOJ1084: [SCOI2005]最大子矩阵
【传送门:BZOJ1084】
简要题意:
给出n行m列的矩阵,要求选出k个不互相覆盖子矩阵,使得选出的k个子矩阵的和最大,求出和
题解:
DP
我的DP方程可能有些麻烦。。
首先面向数据编程,因为m<=2,所以先把m=1的情况求出来,这个就不用讲了
然后对于m=2的情况,我们设:
f[i][k][0]为第i行不选左也不选右,且共有k个子矩阵的最大和
f[i][k][1]为第i行只选择左边成为子矩阵且共有k个子矩阵的最大和
f[i][k][2]为第i行只选择右边成为子矩阵且共有k个子矩阵的最大和
f[i][k][3]为第i行左右边都成为子矩阵,但是并不在同一个子矩阵,且共有k个子矩阵的最大和
f[i][k][4]为第i行左右边都成为子矩阵,而且在同一个子矩阵,且共有k个子矩阵的最大和
状态转移不想写了,太烦了。。自行yy吧
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int f1[110],f2[110],a[110],b[110]; int f[110][11][5]; int s1[110][110],s2[110][110]; int s[110][110]; int main() { int n,m,K; scanf("%d%d%d",&n,&m,&K); int ans=0; if(m==1) { for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) { for(int j=i;j<=n;j++) { int d=0; for(int k=j;k>=i;k--) { d+=a[k]; s[i][j]=max(s[i][j],d); } } } memset(f,0,sizeof(f)); for(int k=1;k<=K;k++) { for(int i=1;i<=n;i++) { for(int j=0;j<i;j++) { f[i][k][0]=max(f[i][k][0],f[j][k-1][0]+s[j+1][i]); ans=max(ans,f[i][k][0]); } } } } else { memset(f,-63,sizeof(f)); for(int i=0;i<=n;i++) for(int k=0;k<=K;k++) f[i][k][0]=0; for(int i=1;i<=n;i++) { scanf("%d%d",&a[i],&b[i]); for(int k=1;k<=K;k++) { //不取 f[i][k][0]=max(f[i-1][k][0],max(f[i-1][k][1],max(f[i-1][k][2],max(f[i-1][k][3],f[i-1][k][4])))); //只取左 f[i][k][1]=max(max(f[i-1][k][1],f[i-1][k][3]),max(f[i-1][k-1][0],max(f[i-1][k-1][2],f[i-1][k-1][4])))+a[i]; //只取右 f[i][k][2]=max(max(f[i-1][k][2],f[i-1][k][3]),max(f[i-1][k-1][0],max(f[i-1][k-1][1],f[i-1][k-1][4])))+b[i]; //左右都取,但左右两边都不在一个子矩阵内 f[i][k][3]=max(f[i-1][k][3],max(f[i-1][k-1][1],f[i-1][k-1][2]))+a[i]+b[i]; if(k>=2) f[i][k][3]=max(f[i][k][3],max(f[i-1][k-2][0],f[i-1][k-2][4])+a[i]+b[i]); //左右都取,但左右两边都在一个子矩阵内 f[i][k][4]=max(f[i-1][k-1][0],max(f[i-1][k-1][1],max(f[i-1][k-1][2],max(f[i-1][k-1][3],f[i-1][k][4]))))+a[i]+b[i]; ans=max(ans,max(f[i][k][0],max(f[i][k][1],max(f[i][k][2],max(f[i][k][3],f[i][k][4]))))); } } } printf("%d\n",ans); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚