坐标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;
}
posted on 2022-08-14 10:57  HZOI-曹蓉  阅读(38)  评论(0编辑  收藏  举报