m<=2。。。
所以直接DP
m=1时
f[i][1][k]表示这一列前i个数选了k个子矩阵(中间的1没有用处)
那么转移就很像最大子段和:f[i][1][k]=max(f[i-1][1][k],f[j][1][k-1]+sum[i]-sum[j]);
前面表示不选择,后面表示选择j-i这一段所能带来的当前情况下的最大得分
m=2时复杂一些
f[i][j][k]表示第一列前i个数第二列前j个数选了k个子矩阵
那么转移分4种情况
1:f[i][j][k]=max(f[i-1][j][k],f[i][j-1][k]);表示继承上一个状态的结果,不选择i或者j
2:f[i][j][k]=max(f[i][j][k],f[ix][j][k-1]+sum[i][1]-sum[ix][1]);表示选择ix-i这一段所能带来的当前情况下的最大得分
3:f[i][j][k]=max(f[i][j][k],f[i][jx][k-1]+sum[j][2]-sum[jx][2]);表示选择jx-j这一段所能带来的当前情况下的最大得分
4:if(i==j)f[i][j][k]=max(f[i][j][k],f[x][x][k-1]+sum[i][1]-sum[x][1]+sum[j][2]-sum[x][2]);表示同时选择两排所能带来的最大得分
最后输出f[n][n][k]就可以了,表示第一列和第二列都选完了,一共选择了k个子矩阵
难得写一次详细的题解……主要是因为我在狂补DP,DP渣到连前几年的普及组题目都写不出来……
去年NOIP D2T2居然不会写
这都坚定了我要补DP的决心……
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int sum[105][5],f[105][105][15],a[105][5],n,m,K;
int main(){
freopen("1084.in","r",stdin);
freopen("1084.out","w",stdout);
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]);
sum[i][j]=sum[i-1][j]+a[i][j];
}
}
memset(f,-127,sizeof(f));
if(m==1){
for(int i=0;i<=n;i++)f[i][1][0]=0;
for(int i=1;i<=n;i++){
for(int k=1;k<=K;k++){
f[i][1][k]=f[i-1][1][k];
for(int j=0;j<i;j++){
f[i][1][k]=max(f[i][1][k],f[j][1][k-1]+sum[i][1]-sum[j][1]);
}
}
}
printf("%d\n",f[n][1][K]);
}
else if(m==2){
for(int i=0;i<=n;i++)for(int j=0;j<=n;j++)f[i][j][0]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=K;k++){
f[i][j][k]=max(f[i-1][j][k],f[i][j-1][k]);
for(int ix=0;ix<i;ix++)f[i][j][k]=max(f[i][j][k],f[ix][j][k-1]+sum[i][1]-sum[ix][1]);
for(int jx=0;jx<j;jx++)f[i][j][k]=max(f[i][j][k],f[i][jx][k-1]+sum[j][2]-sum[jx][2]);
if(i==j)for(int x=0;x<i;x++)f[i][j][k]=max(f[i][j][k],f[x][x][k-1]+sum[i][1]-sum[x][1]+sum[j][2]-sum[x][2]);
}
}
}
printf("%d\n",f[n][n][K]);
}
return 0;
}
评论 (0)