[日常摸鱼]一些DP题(1)
开学停了几天
http://poj.org/problem?id=2279
\(N\)个学生合影,站成左端对齐的 \(k\)排,每排分别有\(N1,N2,\dots,N_k\)个人。 \((N1≥N2≥…≥Nk)\)
第1排站在最后边,第 \(k\) 排站在最前边。 学生的身高互不相同,把他们从高到底依次标记为\(1,2,…,N\)。
在合影时要求每一排从左到右身高递减,每一列从后到前身高也递减。问一共有多少种安排合影位置的方案?(\(k\leq 5,\sum N_i\leq 30\))
线性dp容易给出一个,以及注意一种\(O(30^5)\)的做法,但是这题会炸空间。
这个背景是和整数拆分有关的【杨氏矩阵】,记\(n=\sum N_i\),每个位置\((i,j)\)和所有它右边和下方(如果有)的格子个数(包括自己)称为这个“钩子”的长度,这题对应的答案就是\(n!/\Pi\)钩子长度,证明不会。
$ $
https://www.acwing.com/problem/content/274/
最长上升公共子序列,LIS是\(f[i]\)记录以\(i\)为结尾的LIS,LCS则是记录\(1-i\)和\(1-j\)的,这里就考虑综合起来,\(f[i][j]\)表示以\(s[1,\dots,i]\),以\(t[j]\)为结尾的LCIS的长度,类似LCS,如果\(a[i]\neq b[j]\),直接\(f[i][j]=f[i-1][j]\),否则如果\(a[i]=b[j]\),就有转移\(\begin{aligned}f[i][j]=\max_{k=1,\dots,j-1,b[k]<b[j]}(f[i-1][k])+1\end{aligned}\),又\(b[j]=a[i]\),下面改成\(b[k]<a[i]\),原本\(O(n)\)的转移就可以优化了:枚举\(i\)之后对固定的\(i\),枚举\(j\)的同时更新这个\(max\),整体就能做到\(O(n^2)\)。
$ $
https://www.acwing.com/problem/content/275/
给\(a[]\),需要构造一个单调不减或者单调不增的\(b[]\),最小化\(\sum |a_i-b_i|,n\leq 2000\)。
容易想到先考虑单调不增或者单调不减的其中一种情况,另一种把\(a[]\)翻转过来再做一遍就行。就以考虑单调不减的序列为例,用\(f[i][j]\)表示前\(i\)个,以\(j\)作为\(b[]\)的结尾的最小答案,这样就有\(\begin{aligned}f[i][j]=\min_{k=1,\dots,j}f[i-1][j]+|a_i-j|\end{aligned}\),不过这样值域有点大,注意到任何一个\(b[j]\)的取值,一定是取\(a[]\)中的某个项,不然就浪费了,所以可以离散化一下,复杂度\(O(n^3)\),进一步,这题其实和上题类似,对于一个固定的\(i\),决策集合只增不减,\(O(n)\)的转移可以和枚举\(j\)一起进行,时间复杂度\(O(n^2)\)。
关于\(b[j]\)一定取\(a[]\)中的某个值可以考虑一个数学归纳:假设对\(1,\dots,k-1\)成立,对于\(i=k\)来说,如果\(b[k-1]\leq a[k]\),直接构造\(b[k]=a[k]\)就做到了最优的;否则考虑让\(b[k]=b[k-1]\),根据归纳我们能够说明\(b[k-1]\)取的是\(a[1,\dots,k-1]\),但这还不够,我们需要证明我们取\(b[k]=b[k-1]\)这种决策能够达到最优,想一下也显然,考虑从\(i=k\)往前的一段区间\([j,k]\),这一段的\(b[i]\)都取得相同的\(v\),那这就变成给一个\(a[]\),最小化\(\sum |a_i-v|\)的问题了,这个问题很经典,显然最优情况下的\(v\)能够取得到\(a[]\)中的值。
$ $
https://www.acwing.com/problem/content/276/
\(L\)个点的有向往全图,每条边有边权(即对应\(u\to v\)的代价),有3个人一开始分别在1,2,3处,\(1-n\)个时刻,每个时刻需要有一个人在\(p_i\)这个点,同一个时刻只有同一个时刻一个点不能有多个人,问满足所有需求需要的最小代价。\(L\leq 200,n\leq 1000\)。
考虑DP还是先想怎么记录状态,依然先考虑最暴力的,三个人的位置肯定要能够被记录进去,同时还要记录处理到了哪个时刻,于是就有了\(O(L^3n)\)级别的状态,太大了没法跑。
很套路地想怎么去掉一些状态,任意一个时刻一定会有一个人处于\(p_i\)的位置,于是就可以去掉一个\(L\),比如\(f[i][a][b]\)表示到时刻\(i\),一个人在\(p[i]\),另外两个人分别在\(a,b\)这两个位置,转移也很好想,即\(p_i/a/b\to p_{i+1}\)三种转移方式,实现的时候判定一下状态合法即可。