线性DP

线性DP,即指线性动态规划,是具有线性“阶段”划分的动态规划算法。

假如将“DP”比作我们学习生活中的“函数”,那么“xxDP”就如同其中的一些特定函数,如“三角函数”等。
但是线性DP较其他DP有些不同,其形式更为多样,而不存在某些固定的套路,正如前言所说,只要是具有线性阶段划分的动态规划问题,都可算作线性DP问题。像是“数字三角形”,“LIS”,“LCS”等就是线性DP中最简单的问题。
下面仅举“数字三角形”的伪代码(来水一下)……

for(int i=n-1;i>=1;i--)
    for(int j=1;j<=i;j++)
	dp[i][j]+=max(dp[i+1][j],dp[i+1][j+1]);		

上面的例子虽然简单(难的我也不会啊),却依然能看出线性DP问题解答的思维所在(真的能吗?):将问题转化为多个阶段,每个阶段都可线性地转移到或更新之后的阶段。
下面我们再来看一道简单题。

有 N 个学生合影,站成左端对齐的 k 排,每排分别有 N1,N2,…,Nk 个人。 (N1≥N2≥…≥Nk)
第 1 排站在最后边,第 k 排站在最前边。
学生的身高互不相同,把他们从高到底依次标记为 1,2,…,N。
在合影时要求每一排从左到右身高递减,每一列从后到前身高也递减。
问一共有多少种安排合影位置的方案?(N<=30,K<=5)

其实一开始笔者看到是一脸蒙蔽的(羞愧)……但是在抄书认真学习后,笔者逐渐有了些许思路。
因为K<=5,所以我们可以用一个K元数组来表示该阶段时每一行的已安排的学生人数,即状态。
为什么可以只记录这些呢?因为我们安排学生一定是按照学生的身高单调地选取前x个(x为k元数组的数的和)的,所以记录了这些就知道了“足够的信息”以更新后面的阶段。
那么接下来该怎么更新呢?
有两种思路,即用当前阶段更新之后的阶段或枚举与当前阶段有关的之前阶段来更新当前阶段。
那么因为可以抄书为了方便,我们只思考第一种。

当我们知道当前阶段的状态,需要安排一名新同学时,考虑安排到第i行

  1. ai<Ni(这个很好理解)
  2. i=1 or ai-1>ai(这个是为了保持类似“三角”的形状,还是为了维持单调性)
值得一提的是当K<5时,我们将剩余行的目标定为0,这样只要定义一个5元数组即可。 所以当我们得到dp[a~1~,a~2~,a~3~,a~4~,a~5~]时,只要满足上述条件,就可更新dp[a_1_+1,a~2~,a~3~,a~4~,a~5~],dp[a~1~,a~2~+1,a~3~,a~4~,a~5~]等之后阶段。 只要将初始状态dp[0,0,0,0,0]赋值为1(因为没人也是一种方案),将目标定为dp[N~1~,N~2~,N~3~,N~4~,N~5~],让后就可以愉快地转移了……

下面是伪代码(太丑勿喷)

for(int i1=0;i1<=a[1];i1++){
	for(int i2=0;i2<=a[2];i2++){
		for(int i3=0;i3<=a[3];i3++){
			for(int i4=0;i4<=a[4];i4++){
				for(int i5=0;i5<=a[5];i5++){
					if(i1<a[1]) dp[i1+1][i2][i3][i4][i5]+=dp[i1][i2][i3][i4][i5];
					if(i2<a[2]&&i2<i1) dp[i1][i2+1][i3][i4][i5]+=dp[i1][i2][i3][i4][i5];
					if(i3<a[3]&&i3<i2) dp[i1][i2][i3+1][i4][i5]+=dp[i1][i2][i3][i4][i5];
					if(i4<a[4]&&i4<i3) dp[i1][i2][i3][i4+1][i5]+=dp[i1][i2][i3][i4][i5];
					if(i5<a[5]&&i5<i4) dp[i1][i2][i3][i4][i5+1]+=dp[i1][i2][i3][i4][i5];
				}
			}
		}
	}
}

虽然代码很丑,但是还是能看出解决线性DP问题的一些决策思路(Are you kidding?)……
那么水完了属于是线性DP就此结束。

posted @ 2022-08-13 16:06  mudamudamuda  阅读(261)  评论(0)    收藏  举报