想明白了其实不难 强行瞎扯
这题的限制比较烦,导致了一行行转移几乎不能做(吧)
那么一列列转移呢?
设(f_{i,j,k})表示前(i)列,取(j)个,其中第(i)列取从上往下前(k)个的答案
因为要取到一个砖块,要把该砖块上方以及右上方的先取走,那么如果这一列取(k)个,下一列最少取(k-1)个;反过来,对于列(i),上一列的(k)的取值范围为([1,k+1]),是个前缀,可以前缀最大值优化
写个方程就没了
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define inf 999999999
using namespace std;
const int N=50+10;
il LL rd()
{
re LL x=0,w=1;re char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int n,m,a[N][N],f[N][N*N][N]; //dp用的数组同时用来记录前缀k最大值(懒)
int main()
{
n=rd(),m=rd();
for(int i=1;i<=n;i++)
for(int j=1;j<=n-i+1;j++)
a[i][j]=a[i-1][j]+rd(); //前缀和存储
for(int i=1,t=n;i<=n;i++,t+=n-i+1,t=min(t,m))
for(int j=1;j<=t;j++)
{
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
for(int k=1;k<=n-i+1;k++)
{
if(j-k>=0) f[i][j][k]=max(f[i][j][k],f[i-1][j-k][k+1]+a[k][i]);
}
for(int k=1;k<=n-i+1;k++) f[i][j][k]=max(f[i][j][k],f[i][j][k-1]);
}
printf("%d
",max(f[n][m][0],f[n][m][1]));
return 0;
}