坐标DP
一类典型就是走数位三角形。
eg,给你一个倒三角形似
3 2 1 4
1 2 3
1 2
4
第一行是第一层,要求你如果要到达(i,j)位置(i行j列),必须先到达(i-1,j)和(i-1,j+1)位置。问你最多可以走m步,每到一个格子就可以获得格子里的价值,最多获得多少价值。(n<=50,m<=n*(n+1)/2).
dp[i][j][k]代表到(i,j)位置,走了k步的最大价值,但是本层状态不仅仅和i-1层有关,还和j-1的位置有没有选有关(算价值的delta值的时候),所以我们考虑,i,j<--i-1,j;i-1,j+1竖向连续,没有决策选择的难题,因此本列只和上一列有关,所以我们把三角倒过来,dp[][][]还是一样的含义
4
1 3
2 2 2
3 1 1 4
dp[i][j][k]=max(dp[i-1][p][k-j])[j-1<=p<=i-1].直接O(n^4)转移。
但是注意dp[i][j]当j=1的时候,我们没必要必须从i-1里选,完全可以跳过调到i-2,i-3...里选,所以dp[i]j要特殊处理,把历史状态所有最优都装上。
const int N=55;
int n,m;
int a[N][N],dp[N][N][3000],sum[N][N];
int main()
{
// freopen("a.in","r",stdin);
// freopen("c2.out","w",stdout);
n=re();m=re();
_f(i,1,n){
int len=n-i+1;
f_(j,n,n-len+1)
a[j][i]=re();
}
// _f(i,1,n)
// {
// _f(j,1,i)
// chu("%d ",a[i][j]);
// chu("\n");
// }
//memset(dp,-0x3f,sizeof(dp));
// dp[0][0][0]=0;
_f(i,1,n)
_f(j,1,i)
sum[i][j]=sum[i][j-1]+a[i][j];
int ans=0;
_f(i,1,n)//现在是第几行
{
//选0个单独处理
_f(j,0,i-1)//枚举上一行选了几个(也可以0)
_f(k,j,m)//为止走了几步
dp[i][0][k]=max(dp[i][0][k],dp[i-1][j][k]);
_f(j,1,i)//要选多少个连续的j
{
//chu("rand:%d will chose %d 个连续j\n",i,j);
int cst=min(i*(i-1)/2+j,m),kjh=max(j,j*(j+1)/2);
//chu("(到目前m)fr:%d to:%d\n",kjh,cst);
for(rint k=kjh;k<=cst;++k)//选多少块
{
for(rint p=j-1;p<=i-1;++p)//上一层选多少个,,上一层选0个也行(如果j=1)
dp[i][j][k]=max(dp[i][j][k],dp[i-1][p][k-j]+sum[i][j]);
if(k<=m)ans=max(ans,dp[i][j][k]);
//chu("dp[%d][%d][%d]:%d\n",i,j,k,dp[i][j][k]);
}
}
}
chu("%d",ans);
return 0;
}