P1437 [HNOI2004]敲砖块(dp)

从上往下和从下往上递推都具有后效性。

考虑从右往左递推。

因为当选择 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][kj])

注意 l l l的范围是 [ m a x ( j − 1 , 0 ) , n − i ] [max(j-1,0),n-i] [max(j1,0),ni]

时间复杂度: 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;
}