luogu P1437 [HNOI2004]尻♂砖块

传送门

想明白了其实不难 强行瞎扯

这题的限制比较烦,导致了一行行转移几乎不能做(吧)

那么一列列转移呢?

\(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\n",max(f[n][m][0],f[n][m][1]));
  return 0;
}


posted @ 2018-09-20 17:32  ✡smy✡  阅读(140)  评论(0编辑  收藏  举报