从上往下和从下往上递推都具有后效性。
考虑从右往左递推。
因为当选择 a i , j a_{i,j} ai,j时,该列上面的所有块都选,右边的块也都要选。
令 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示选择到第 i i i列第 j j j行选择了 k k k个块的最大值。
考虑如何从第 i + 1 i+1 i+1列转移。
有: f [ i ] [ j ] [ k ] = m a x ( f [ i ] [ j ] [ k ] , f [ i + 1 ] [ l ] [ k − j ] ) f[i][j][k]=max(f[i][j][k],f[i+1][l][k-j]) f[i][j][k]=max(f[i][j][k],f[i+1][l][k−j])
注意 l l l的范围是 [ m a x ( j − 1 , 0 ) , n − i ] [max(j-1,0),n-i] [max(j−1,0),n−i]
时间复杂度: O ( n 3 m ) O(n^3m) O(n3m)
code
// Problem: P1437 [HNOI2004]敲砖块
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1437
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-07-03 11:02:47
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=55,M=3e3+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
int n,m,f[N][N][M],a[N][N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n-i+1;j++) scanf("%d",&a[i][j]);
mst(f,0xc0);f[n+1][0][0]=0;
for(int i=n;i;i--)
for(int j=0,sum=0;j<=n-i+1;j++,sum+=a[j][i])
for(int k=j;k<=m;k++)
for(int l=max(0,j-1);l<=n-i;l++)
f[i][j][k]=max(f[i][j][k],f[i+1][l][k-j]+sum);
int ans=-inf;
for(int i=1;i<=n;i++)
for(int j=0;j<=n-i+1;j++)
ans=max(ans,f[i][j][m]);
printf("%d\n",ans);
return 0;
}