我是靠谱客的博主 成就香菇,最近开发中收集的这篇文章主要介绍NOIP复健计划——动态规划,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

树形DP

[POI2011] DYN-Dynamite

二分 K K K

check(mid):
能否选出 m m m个点,使得 ∀ i 为关键点, M i n j   i s   s e l e c t e d { d i s ( i , j ) } ≤ m i d forall i为关键点,Min_{j is selected}{dis(i,j)}leq mid i为关键点,Minj is selected{dis(i,j)}mid

进一步转化问题:
一棵树上选 t o t tot tot 个点,使得 ∀ i 为关键点, M i n j   i s   s e l e c t e d { d i s ( i , j ) } ≤ m i d forall i为关键点,Min_{j is selected}{dis(i,j)}leq mid i为关键点,Minj is selected{dis(i,j)}mid,最小化 t o t tot tot。若 t o t ≤ m totleq m totm则 check(mid) 为 true,否则 check(mid) 为 false

定义点 i i i被点 j j j覆盖,当且仅当 d i s i , j ≤ m i d dis_{i,j}leq mid disi,jmid
假设当前考虑到以 x x x为根的子树。
对于在 x x x子树内的关键点,它们要么被 x x x子树内的点覆盖,要么还等着被 x x x子树外的点覆盖。
对于第二种情况,我们需要把问题留到考虑 x x x的祖先时再去解决。

f x f_x fx表示 x x x子树内未被覆盖点到 x x x的最远距离,
g x g_x gx表示 x x x子树内被选择点到 x x x的最近距离。
初值: f x = − ∞ , g x = ∞ f_{x}=-infty,g_{x}=infty fx=,gx=
转移: f x = max ⁡ { f y + 1 } f_x=max {f_y+1} fx=max{fy+1} g x = min ⁡ { f y + 1 } g_x=min{f_y+1} gx=min{fy+1}
特判:
f x + g x ≤ m i d f_x+g_xleq mid fx+gxmid,则 x x x子树内所有点都已经被覆盖, f x ← − ∞ f_xleftarrow -infin fx
f x = m i d f_x=mid fx=mid,则 x x x一定要选, f x ← − ∞ , g x ← 0 , t o t + + f_xleftarrow -infin,g_xleftarrow 0,tot++ fx,gx0,tot++
g x > m i d   a n d   x 为关键点 g_x>mid and x为关键点 gx>mid and x为关键点,把问题留给 x x x的父亲解决, f x ← m a x ( f x , 0 ) f_xleftarrow max(f_x,0) fxmax(fx,0)
Code

重建道路

f [ i ] [ j ] f[i][j] f[i][j]表示以 i i i为根的子树,保留 j j j个点(包括自己),删去的最小边数
初始化: f [ i ] [ 1 ] = s o n [ i ] f[i][1]=son[i] f[i][1]=son[i] s o n [ i ] son[i] son[i]表示 i i i有几个儿子)
转移: f [ u ] [ j ] = m a x { f ( u , j − k ) + f ( v , k ) − 1 } f[u][j]=max{f(u,j-k)+f(v,k)-1} f[u][j]=max{f(u,jk)+f(v,k)1}(−1是因为v和u之间相连的那一条边不删)

注意:我们要获得的子树的根并不一定要是整棵树的根节点
Code

城市环路

基环树
如果这道题是出在树上,那就是最大独立集的裸题
f [ i ] [ 1 / 0 ] f[i][1/0] f[i][1/0]表示选/不选 i i i ,以 i i i为根的子树的最大贡献
f [ u ] [ 1 ] = ∑ v ∈ s o n u f [ v ] [ 0 ] f[u][1]=sum_{vin son_u}f[v][0] f[u][1]=vsonuf[v][0]
f [ u ] [ 0 ] = ∑ v ∈ s o n u m a x ( f [ v ] [ 0 ] , f [ v ] [ 1 ] ) f[u][0]=sum_{vin son_u}max(f[v][0],f[v][1]) f[u][0]=vsonumax(f[v][0],f[v][1])

考虑在基环树上怎么做:
法一:强制断边
首先把环找出来
对于环上一对相邻点 ( x , y ) (x,y) (x,y),根据题意, x , y x,y x,y不能同时选

  1. 强制 x x x不选,此时 y y y可选可不选,断掉边 ( x , y ) (x,y) (x,y)无影响。断边后做树形dp, f [ x ] [ 0 ] f[x][0] f[x][0]即为此情况下的答案
  2. 强制 y y y不选,此时 x x x可选可不选,断掉边 ( x , y ) (x,y) (x,y)无影响。断边后做树形dp, f [ y ] [ 0 ] f[y][0] f[y][0]即为此情况下的答案
  3. 最终答案= m a x ( f [ x ] [ 0 ] , f [ y ] [ 0 ] ) max(f[x][0],f[y][0]) max(f[x][0],f[y][0])

Code
法二:树形dp+环形dp
博客
Code

[JSOI2008]魔兽地图

这个题的特殊性在于儿子对于父亲节点是有影响的
所以用 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示第 i i i号装备,其中 j j j被祖先用来合成上层装备,花费 k k k元所能获得最大的力量值。

分步转移,第一步先枚举第 i i i 号装备合成多少个,第二步再合并子树贡献。
Code

[JSOI2016]最佳团体

分数规划
二分答案 m i d mid mid,问题转化为:
给定一棵根为0的树。每点点权为 P i − m i d ∗ S i P_i-mid *S_i PimidSi,选定一个点就必须选其所有祖先节点。
问能否取 k k k个节点使得取出点的点权和大于零。

d p [ u ] [ j ] dp[u][j] dp[u][j]表示以 u u u为根的子树,取 j j j个节点所带来的最大收益
合并就是经典树形背包
Code

[USACO12FEB]Nearby Cows G

基础换根dp
f [ i ] [ j ] f[i][j] f[i][j] 表示 i i i 子树内到 i i i 距离为 j j j 的点的权值和
g [ i ] [ j ] g[i][j] g[i][j] 表示 i i i 子树外到 i i i 距离为 j j j 的点的权值和
Code

CF512D Fox And Travelling

首先,在环中的点一定不会被遍历
用类似拓扑排序的过程可以把环上的点全部扔掉,剩下的点会构成若干个有根树和无根树,其中有根树的根是树中唯一与环中的点相连的点

先考虑有根树,设 f i , j f_{i,j} fi,j 表示以 i i i 为根的子树中,选 j j j 个点的方案数
转移: f i , j + k = f p i , j × f s o n , k × ( j + k k ) f_{i,j+k}=fp_{i,j}times f_{son,k}times binom {j+k}{k} fi,j+k=fpi,j×fson,k×(kj+k)
特别地, f i , s i z i = f i , s i z i − 1 f_{i,siz_i}=f_{i,siz_i-1} fi,sizi=fi,sizi1(根节点必须最后删)

对于无根树,感觉可以钦定最后一个被删的是哪个点,但更简单的方法是直接对每个点为根跑一遍有根树的算法,然后发现选 i i i 个点的情况在没被选的 s i z − i siz-i sizi 个点为根的时候会被统计,因此答案要除以 s i z − i siz-i sizi(如果全选是特殊情况,不需要除)

然后就是森林合并答案,就用类似合并子树的方法组合数合并就可以了
Code

CF543D Road Improvement

换根dp
根据套路,
我们设 f u f_u fu为修好 u u u为根的子树中的路的方案数
g u g_u gu为修好整棵树中除了以 u u u为根的子树中的路的方案数

先考虑 f u f_u fu怎么求:
f u , 0 f_{u,0} fu,0表示 u u u子树内无坏边的方案数, f u , 1 f_{u,1} fu,1表示 u u u子树内有坏边的方案数
f u , 0 = ∏ v ∈ s o n u f v , 0 , f u , 1 = ∏ v ∈ s o n u ( f v , 0 × 2 + f v , 1 ) − 1 f_{u,0}=prod_{vin son_u} f_{v,0},f_{u,1}=prod_{vin son_u} (f_{v,0}times 2+f_{v,1})-1 fu,0=vsonufv,0,fu,1=vsonu(fv,0×2+fv,1)1
发现 f u , 0 = 1 f_{u,0}=1 fu,0=1,又考虑到 f u = f u , 0 + f u , 1 f_u=f_{u,0}+f_{u,1} fu=fu,0+fu,1
所以 f u = ∏ v ∈ s o n u ( f v + 1 ) f_u=prod_{vin son_u}(f_v+1) fu=vsonu(fv+1)

再考虑 g u g_u gu
g u = g f a × f f a f u + 1 + 1 = g f a × ∏ x ∈ s o n f a , x ≠ u ( f x + 1 ) + 1 g_u=frac{g_{fa}times f_{fa}}{f_u+1}+1=g_{fa}timesprod_{xin son_{fa},xnot=u}(f_x+1)+1 gu=fu+1gfa×ffa+1=gfa×xsonfa,x=u(fx+1)+1
因为逆元不保证存在,所以要记录前缀积和后缀积来避免除法
Code

CF1146F Leaf Partition

博客

CF1016F Road Projects

博客

CF671D Roads in Yusland

博客

CF337D Book of Evil

经典换根dp
f [ u ] [ 0 ] f_[u][0] f[u][0]表示 u u u子树内的点到 u u u最大/次大距离
g [ u ] [ 0 ] g_[u][0] g[u][0]表示 u u u子树外的点到 u u u最大/次大距离

CF23E Tree

博客

CF708C Centroids

博客

其他DP

[WC2008] 游览计划

斯坦纳树
模板

斯坦纳树问题:

给出一个有 n n n 个点 m m m 条边的有权无向图,然后选定 k k k 个点,让你求出使这 k k k 个点联通所需的最小代价

求解:

首先,答案的子图一定是树

d p [ i ] [ S ] dp[i][S] dp[i][S] 表示以 i i i 为根的一棵树,包含集合 S S S 中所有点的最小代价。

转移:

对于 i i i 的度数为 1 的情况,可以考虑枚举树上与 i i i 相邻的点 j j j,则
d p ( j , s ) + w ( j , i ) − > d p ( i , s ) dp(j,s)+w(j,i)−>dp(i,s) dp(j,s)+w(j,i)>dp(i,s)
对于 i i i 的度数大于 1 的情况,可以划分成几个子树考虑,则
d p ( i , T ) + d p ( i , S − T ) − > d p ( i , S ) ( T ⊆ S ) dp(i,T)+dp(i,S−T)−>dp(i,S)(Tsubseteq S) dp(i,T)+dp(i,ST)>dp(i,S)(TS)

对于第1种转移,可以想到最短路模型的三角形不等式,对于每一个 S S S ,将图做一次松弛操作即可。具体可以用 dijkstra 或 spfa 实现。
对于第2种转移,枚举子集即可。

如何优秀的枚举子集:
一个非常trivial的做法:

for(int sta=0;sta<=MAX;sta++){
	for(int s=0;s<=MAX;s++){
		if(s&sta==s){
			//to do sth
		}
	}
}

时间复杂度 O ( 4 n ) O(4^n) O(4n)
更优秀的实现:

for(int sta=0;sta<=MAX;sta++){
	for(int s=(sta-1)&sta;s;s=(s-1)&sta){
		//to do sth
	}
}

时间复杂度 ∑ s t a 2 ∣ ∣ s t a ∣ ∣ = ∑ i = 0 n C n i 2 i = 3 n sum_{sta}2^{||sta||}=sum_{i=0}^{n}C_{n}^{i}2^i=3^n sta2∣∣sta∣∣=i=0nCni2i=3n
Code

[COCI2011-2012 6]

SOS DP+容斥

首先发现 m m m 很小,于是直接对于每一个箱子状压
于是问题转化为选若干个数使它们按位或为全集
继续转化一下,把每个箱子取个反,问题就变成了选若干个数使它们按位与为空集

g [ S ] g[S] g[S] 为有多少种选数方案使 S S S为选出数的按位与的子集
答案为 ∑ S ( − 1 ) ∣ ∣ S ∣ ∣ g [ S ] sum_{S}(-1)^{||S||}g[S] S(1)∣∣S∣∣g[S]

f [ S ] f[S] f[S] 为有多少个箱子,使 S S S为其代表的数 A [ i ] A[i] A[i]的子集
g [ S ] = 2 f [ S ] − 1 g[S]=2^{f[S]}-1 g[S]=2f[S]1

f [ S ] f[S] f[S]
法一:直接枚举子集

for(int sta=0;sta<=MAX;sta++){
	for(int s=(sta-1)&sta;s;s=(s-1)&sta){
		//to do sth
	}
}

时间复杂度 O ( 3 m ) O(3^m) O(3m)
法二:高维前缀和解决这类子集问题

for(int i=0;i<m;i++){
	for(int s=0;s<(1<<m);s++){
		if(s&(1<<i)){
			f[s^(1<<i)]+=f[s];//与标准sos dp方程相比,这里的语句反过来了
		}
	}
}

时间复杂度 O ( m 2 m ) O(m2^m) O(m2m)
这种方法又称为SOS DP:博客1 博客2
Code

[CF383E] Vowels

法一:​SOS DP
正难则反,考虑统计不合法单词数
让每个单词对应一个状态 A A A,第 i i i位表示第 i i i个字母是否在单词中出现(1为出现)
记元音字母集的补集为 B B B
则不合法单词对应的 A A A均为 B B B的子集
f [ S ] f[S] f[S]表示有多少个单词,其对应的 A A A S S S的子集,可以用sos dp求出:

for(int i=0;i<m;i++){
	for(int s=0;s<(1<<m);s++){
		if(s&(1<<i)){
			f[s]+=f[s^(1<<i)];//标准sos dp方程
		}
	}
}

答案即为 ( n − f [ S ] ) 2 (n-f[S])^2 (nf[S])2的异或和
Code

法二:SOS DP+容斥
g [ S ] g[S] g[S]为元音集合为 S S S时的合法单词数
f [ s ] f[s] f[s]为有多少个单词,使 s s s为其代表的 A A A的子集
f [ s ] = ∑ i = 1 n [ s ∈ w o r d i ] f[s]=sum_{i=1}^{n}[sin word_i] f[s]=i=1n[swordi]
g [ S ] = ∑ s ∈ S ( − 1 ) ∣ ∣ s ∣ ∣ + 1 f [ s ] g[S]=sum_{sin S}(-1)^{||s||+1}f[s] g[S]=sS(1)∣∣s∣∣+1f[s]
但是这样做的时间复杂度是 O ( 3 24 ) O(3^{24}) O(324),会TLE,所以我们转换一下:
f [ s ] = ∑ i = 1 n ( − 1 ) ∣ ∣ s ∣ ∣ + 1 [ s ∈ w o r d i ] f[s]=sum_{i=1}^{n}(-1)^{||s||+1}[sin word_i] f[s]=i=1n(1)∣∣s∣∣+1[swordi]
g [ S ] = ∑ s ∈ S f [ s ] g[S]=sum_{sin S}f[s] g[S]=sSf[s]
f [ s ] f[s] f[s]的复杂度是 O ( n ⋅ 2 3 ) O(ncdot 2^3) O(n23)(对于每一个单词,枚举其子集)
g [ S ] g[S] g[S]的复杂度是 O ( 24 ⋅ 2 24 ) O(24cdot 2^{24}) O(24224)(sos dp)
Code

[CF449D] Jzzhu and Numbers

法一:SOS DP+容斥
同COCI2011-2012 6
g [ S ] g[S] g[S] 为有多少种选数方案使 S S S为选出数的按位与的子集
答案为 ∑ S ( − 1 ) ∣ ∣ S ∣ ∣ g [ S ] sum_{S}(-1)^{||S||}g[S] S(1)∣∣S∣∣g[S]
f [ S ] f[S] f[S] 为有多少个箱子,使 S S S为其代表的数 A [ i ] A[i] A[i]的子集
g [ S ] = 2 f [ S ] − 1 g[S]=2^{f[S]}-1 g[S]=2f[S]1
Code

法二:FWT
d p [ i ] [ s ] dp[i][s] dp[i][s]为从前 i i i个数中选择,有多少种选数方案使选出数的按位与为 s s s
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + ∑ k & a [ i ] = = j d p [ i − 1 ] [ k ] dp[i][j]=dp[i-1][j]+sum_{k&a[i]==j}dp[i-1][k] dp[i][j]=dp[i1][j]+k&a[i]==jdp[i1][k]
d p [ s & a [ i ] ] + = d p [ s ] dp[s&a[i]]+=dp[s] dp[s&a[i]]+=dp[s]
发现这个柿子本质上就是一个和卷积,可以用FWT解决
博客

[CF348D] Turtles

LGV引理
Code

[USACO20OPEN]Exercise G

循环了几次可以回到原地,当且仅当形成了一个环,假设每个环的长度是 a i a_i ai,那么 k = lcm ⁡ a i k=operatorname{lcm}a_i k=lcmai

又知道 lcm ⁡ a i operatorname{lcm}a_i lcmai 其实就是将所有 a i a_i ai分解质因数,然后取每个质数的最高次幂乘起来,
所以我们可以想到枚举素数来做dp

f [ i ] [ j ] f[i][j] f[i][j] 表示前 i i i 个素数总和为 j j j 的所有 K K K 的总和

枚举第 i i i 个素数的幂进行转移:
f [ i ] [ j ] = ∑ f [ i − 1 ] [ j − p i k ] × p i k f[i][j]=sum f[i-1][j-p_i^k]times p_i^k f[i][j]=f[i1][jpik]×pik

可以滚动数组减掉一维
Code

双倍经验:[SCOI2009]游戏 博客

OSU!

d p [ i ] dp[i] dp[i]表示到了第 i i i个位置的期望得分,
f [ l ] f[l] f[l]表示第 i i i个位置之前有长度为 l l l的连续1串的概率,
p [ i ] p[i] p[i]表示 i i i为1的概率,
则有转移:
d p [ i ] = d p [ i − 1 ] + p [ i ] ∑ l f [ l ] ( ( l + 1 ) 3 − l 3 ) = d p [ i − 1 ] + p [ i ] ∑ l f [ l ] ( 3 l 2 + 3 l + 1 ) dp[i]=dp[i-1]+p[i]sum_lf[l]((l+1)^3-l^3)=dp[i-1]+p[i]sum_l f[l](3l^2+3l+1) dp[i]=dp[i1]+p[i]lf[l]((l+1)3l3)=dp[i1]+p[i]lf[l](3l2+3l+1)

于是考虑维护
E l = ∑ l f [ l ] ⋅ l E_l=sum_lf[l]cdot l El=lf[l]l
E l 2 = ∑ l f [ l ] ⋅ l 2 E_{l^2}=sum_lf[l]cdot l^2 El2=lf[l]l2

则总转移方程为:
E l [ i ] = p [ i ] ∑ l f [ l ] ( l + 1 ) = p [ i ] ( E l [ i − 1 ] + ∑ l f [ l ] ) = p [ i ] ( E l [ i − 1 ] + 1 ) E_l[i]=p[i]sum_lf[l](l+1)=p[i](E_l[i-1]+sum_lf[l])=p[i](E_l[i-1]+1) El[i]=p[i]lf[l](l+1)=p[i](El[i1]+lf[l])=p[i](El[i1]+1)
E l 2 [ i ] = p [ i ] ∑ l f [ l ] ( l 2 + 2 l + 1 ) = p [ i ] ( E l 2 [ i − 1 ] + 2 E l [ i − 1 ] + 1 ) E_{l^2}[i]=p[i]sum_lf[l](l^2+2l+1)=p[i](E_{l^2}[i-1]+2E_{l}[i-1]+1) El2[i]=p[i]lf[l](l2+2l+1)=p[i](El2[i1]+2El[i1]+1)
d p [ i ] = d p [ i − 1 ] + p [ i ] ( 3 E l 2 [ i − 1 ] + 3 E l [ i − 1 ] + 1 ) dp[i]=dp[i-1]+p[i](3E_{l^2}[i-1]+3E_l[i-1]+1) dp[i]=dp[i1]+p[i](3El2[i1]+3El[i1]+1)
Code

收集邮票

f [ i ] f[i] f[i]表示已经取到了 i i i种邮票,要取完剩余邮票的期望次数,
a n s [ i ] ans[i] ans[i]表示已经取到了 i i i种邮票,要取完剩余邮票的期望价格

f [ i ] = i n f [ i ] + n − i n f [ i + 1 ] + 1 f[i]=frac{i}{n}f[i]+frac{n-i}{n}f[i+1]+1 f[i]=nif[i]+nnif[i+1]+1
⇒ f [ i ] = f [ i + 1 ] + n n − i Rightarrow f[i]=f[i+1]+frac{n}{n-i} f[i]=f[i+1]+nin
a n s [ i ] = i n ( a n s [ i ] + f [ i ] + 1 ) + n − i n ( a n s [ i + 1 ] + f [ i + 1 ] + 1 ) ans[i]=frac{i}{n}(ans[i]+f[i]+1)+frac{n-i}{n}(ans[i+1]+f[i+1]+1) ans[i]=ni(ans[i]+f[i]+1)+nni(ans[i+1]+f[i+1]+1)
⇒ a n s [ i ] = a n s [ i + 1 ] + f [ i + 1 ] + i n − i f [ i ] + n n − i Rightarrow ans[i]=ans[i+1]+f[i+1]+frac{i}{n-i}f[i]+frac{n}{n-i} ans[i]=ans[i+1]+f[i+1]+niif[i]+nin

注意: a n s [ i ] ≠ ( f [ i ] + 1 ) f [ i ] 2 ans[i]not=frac{(f[i]+1)f[i]}{2} ans[i]=2(f[i]+1)f[i],理由如下:
a n s [ i ] = ∑ c n t p c n t ( c n t + 1 ) c n t 2 ans[i]=sum_{cnt}p_{cnt}frac{(cnt+1)cnt}{2} ans[i]=cntpcnt2(cnt+1)cnt
( f [ i ] + 1 ) f [ i ] 2 = ( ∑ c n t p c n t c n t + 1 ) ∑ c n t p c n t c n t 2 frac{(f[i]+1)f[i]}{2}=frac{(sum_{cnt}p_{cnt}cnt+1)sum_{cnt}p_{cnt}cnt}{2} 2(f[i]+1)f[i]=2(cntpcntcnt+1)cntpcntcnt
Code

[CF605E] Intergalaxy Trips

f [ i ] f[i] f[i]为从 i i i走到 n n n的期望耗时

考虑转移:
首先,我们一定不会找比当前点更劣的点转移,因为这还没有留在原地划算
其次,如果可以转移,那么转移到某点的唯一可能是比它优的点都转移不到
f [ i ] = 1 + f [ i ] ∏ j , f j < f i ( 1 − p i , j ) + ∑ j , f j < f i f [ j ] p i , j ∏ k , p k < p j ( 1 − p i , k ) f[i]=1+f[i]prod_{j,f_j<f_i}(1-p_{i,j})+sum_{j,f_j<f_i}f[j]p_{i,j}prod_{k,p_k<p_j}(1-p_{i,k}) f[i]=1+f[i]j,fj<fi(1pi,j)+j,fj<fif[j]pi,jk,pk<pj(1pi,k)

⇒ f [ i ] = 1 + ∑ j , f j < f i f [ j ] p i , j ∏ k , p k < p j ( 1 − p i , k ) 1 − ∏ j , f j < f i ( 1 − p i , j ) Rightarrow f[i]=frac{1+sum_{j,f_j<f_i}f[j]p_{i,j}prod_{k,p_k<p_j}(1-p_{i,k})}{1-prod_{j,f_j<f_i}(1-p_{i,j})} f[i]=1j,fj<fi(1pi,j)1+j,fj<fif[j]pi,jk,pk<pj(1pi,k)

实现:朴素版Dijkstra
博客
Code

常见优化与综合运用

[CF939F] Cutlet

d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 r i r_i ri 分钟,把现在煎的面叫做正面,背面煎了 j j j 分钟的最小翻面次数
显然只会在一个区间至多翻 2 次:
不翻面, d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i-1][j] dp[i][j]=dp[i1][j]
翻了一次,枚举正面煎了 k k k 秒, d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ r − j − k ] + 1 ) dp[i][j] = min(dp[i-1][r-j-k] + 1) dp[i][j]=min(dp[i1][rjk]+1)
翻了两次,枚举背面煎了 k k k 秒, d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j − k ] + 2 ) dp[i][j] = min(dp[i-1][j-k] + 2) dp[i][j]=min(dp[i1][jk]+2)
在这里插入图片描述
分别用单调队列维护即可

一句话解释单调队列优化dp:如果一个人比你年轻,还比你强,那你就永远打不过他了.jpg

Code

[CF311B] Cats Transport

因为对于任意一只猫,我们都可以直接算出如果有一个人要恰好接走它,需要在哪一时刻出发,我们设第i只猫对应的这个时刻为 t i t_{i} ti

对于第 i i i只猫,我们定义 t i t_{i} ti表示如果有人在 t i t_{i} ti时刻出发,此猫无须等待即可被接走
题目转化为:
现有 p p p个人,要求为每个人指定一个出发时间 a j a_j aj,最小化 ∑ i = 1 m ( x i − t i ) sum_{i=1}^{m}(x_i-t_i) i=1m(xiti) x i = M i n a j ≥ t i { a j } x_i=Min_{a_jgeq t_i}{a_j} xi=Minajti{aj}

观察到每个人的出发时间 a j a_j aj必是 t i t_i ti中的一个
那么假设第 x x x个人的出发时间是 t i t_i ti,第 x − 1 x-1 x1个人的出发时间是 t j t_j tj
则第 x x x个人会接走第 j + 1 j+1 j+1到第 i i i只猫
题目转化为:
现有 p p p个人,要求为每个人选一只猫 b j b_j bj,最小化 ∑ j = 1 p ∑ i = b j − 1 + 1 b j ( t b j − t i ) sum_{j=1}^{p}sum_{i=b_{j-1}+1}^{b_j}(t_{b_j}-t_i) j=1pi=bj1+1bj(tbjti)

f [ i ] [ j ] f[i][j] f[i][j]表示考虑到第 i i i个人,这个人选择了第 j j j只猫的最小代价
s [ i ] = ∑ k = 1 i t k s[i]=sum_{k=1}^{i}t_k s[i]=k=1itk
f [ i ] [ j ] f[i][j] f[i][j]有转移式:
f [ i ] [ j ] = M i n { f [ i − 1 ] [ k ] + t j ( j − k ) − ( s [ j ] − s [ k ] ) } f[i][j]=Min{f[i-1][k]+t_j(j-k)-(s[j]-s[k])} f[i][j]=Min{f[i1][k]+tj(jk)(s[j]s[k])}

斜率优化 f [ i ] [ j ] f[i][j] f[i][j]
f [ i ] [ j ] = f [ i − 1 ] [ k ] + t j ( j − k ) − ( s [ j ] − s [ k ] ) f[i][j]=f[i-1][k]+t_j(j-k)-(s[j]-s[k]) f[i][j]=f[i1][k]+tj(jk)(s[j]s[k])
   ⟺    f [ i − 1 ] [ k ] + s [ k ] = t j ⋅ k + f [ i ] [ j ] + s [ j ] − t j j iff f[i-1][k]+s[k]=t_jcdot k+f[i][j]+s[j]-t_jj f[i1][k]+s[k]=tjk+f[i][j]+s[j]tjj
f [ i − 1 ] [ k ] + s [ k ] f[i-1][k]+s[k] f[i1][k]+s[k]看成 y y y k k k看成 x x x t j t_j tj看成 k k k f [ i ] [ j ] + s [ j ] − t j j f[i][j]+s[j]-t_jj f[i][j]+s[j]tjj看成 b b b
现要找到一个点 P ( k , f [ i − 1 ] [ k ] + s [ k ] ) P(k,f[i-1][k]+s[k]) P(k,f[i1][k]+s[k])使得 b b b最小
维护一个下凸壳,并在凸包上找最优决策点 P P P即可
因为 k k k单增,所以可以用单调队列实现
Code

[NOI2009] 诗人小G

f i f_i fi表示对前 i i i个句子排版后的最小不协调度
s i = ∑ j = 1 i ( l e n j + 1 ) s_i=sum_{j=1}^i(len_j+1) si=j=1i(lenj+1),则
f i = m i n j = 0 i − 1 { f j + ∣ s i − s j − 1 − L ∣ P } f_i=min_{j=0}^{i-1}{f_j+|s_i-s_j-1-L|^P} fi=minj=0i1{fj+sisj1LP}
这条转移式是有决策单调性的

感性证明:
h j ( x ) = ∣ x − s j − 1 − L ∣ P h_j(x)=|x-s_j-1-L|^P hj(x)=xsj1LP
不难看出 h j h_j hj关于直线 x = s j + 1 + L x=s_j+1+L x=sj+1+L对称

对于 x > s j + 1 + L x>s_j+1+L x>sj+1+L的部分:
h j ( x ) = ( x − s j − 1 − L ) P h_j(x)=(x-s_j-1-L)^P hj(x)=(xsj1L)P
h j ′ ( x ) = P ( x − s j − 1 − L ) P − 1 h_j'(x)=P(x-s_j-1-L)^{P-1} hj(x)=P(xsj1L)P1
所以 h j ′ ( x ) h_j'(x) hj(x) ( s j + 1 + L , + ∞ ) (s_j+1+L,+infin) (sj+1+L,+)上单增且恒大于0

对于 x < s j + 1 + L x<s_j+1+L x<sj+1+L的部分:
根据对称性, h j ′ ( x ) h_j'(x) hj(x) ( − ∞ , s j + 1 + L ) (-infin,s_j+1+L) (,sj+1+L)上同样单增且恒小于0

又因为函数连续,所以 h j ′ ( x ) h_j'(x) hj(x) R R R上单增

又因为 h j 1 ( x ) = h j ( x + s j − s j 1 ) h_{j_1}(x)=h_j(x+s_j-s_{j_1}) hj1(x)=hj(x+sjsj1),即不同的 h j ( x ) h_j(x) hj(x)之间可以通过平移得到,
所以 ∀ j 1 ≠ j 2 forall j_1not=j_2 j1=j2 h j 1 ( x ) h_{j_1}(x) hj1(x) h j 2 ( x ) h_{j_2}(x) hj2(x)最多一个交点
在这里插入图片描述
意会一下吧
所以 f f f 的转移式满足决策单调性
%%%FlashHu大佬

严谨证明:
证明 w ( j , i ) = ∣ s i − s j − 1 − L ∣ P w(j,i)=|s_i-s_j-1-L|^P w(j,i)=sisj1LP满足四边形不等式,本蒟蒻不会
贴个博客吧
Code

[CF868F] Yet Another Minimization Problem

d p i , j dp_{i,j} dpi,j表示将序列的前 i i i个数划分成 j j j段所需要的最小费用,
w l , r w_{l,r} wl,r表示将 l l l r r r划分成一段所需要的花费
显然有转移方程: d p i , j = m i n { d p x , j − 1 + w x + 1 , i } dp_{i,j} =min{dp_{x,j-1}+w_{x+1,i}} dpi,j=min{dpx,j1+wx+1,i}

决策单调性证明:
法一(四边形不等式):
要证 d p dp dp的转移具有决策单调性
即证 ∀ a < b , w ( a , b ) + w ( a + 1 , b + 1 ) ≤ w ( a , b + 1 ) + w ( a + 1 , b ) forall a<b,w(a,b)+w(a+1,b+1)leq w(a,b+1)+w(a+1,b) a<b,w(a,b)+w(a+1,b+1)w(a,b+1)+w(a+1,b)
即证 ∀ a < b , w ( a , b ) − w ( a + 1 , b ) ≤ w ( a , b + 1 ) − w ( a + 1 , b + 1 ) forall a<b,w(a,b)-w(a+1,b)leq w(a,b+1)-w(a+1,b+1) a<b,w(a,b)w(a+1,b)w(a,b+1)w(a+1,b+1)
即把区间从 [ a + 1 , b ] [a+1,b] [a+1,b]拓到 [ a , b ] [a,b] [a,b]的增量 ≤ leq 把区间从 [ a + 1 , b + 1 ] [a+1,b+1] [a+1,b+1]拓到 [ a , b + 1 ] [a,b+1] [a,b+1]的增量
该式显然正确,因为在区间中增加一个元素,它对答案的贡献与区间内与它值相同的元素数量有关
法二:
c x [ i ] c_x[i] cx[i]为区间 [ 1 , i ] [1,i] [1,i]中值为 j j j的元素个数
h j ( i ) = w ( j + 1 , i ) = ∑ x ( c x [ i ] − c x [ j ] ) ( c x [ i ] − c x [ j ] − 1 ) 2 h_j(i)=w(j+1,i)=sum_{x}frac{(c_x[i]-c_x[j])(c_x[i]-c_x[j]-1)}{2} hj(i)=w(j+1,i)=x2(cx[i]cx[j])(cx[i]cx[j]1)
因为 ( c x [ i ] − c x [ j ] ) ( c x [ i ] − c x [ j ] − 1 ) 2 frac{(c_x[i]-c_x[j])(c_x[i]-c_x[j]-1)}{2} 2(cx[i]cx[j])(cx[i]cx[j]1)的导函数单增,所以合起来就可以得出 h j ′ ( i ) h_j'(i) hj(i)单增
结合图像可以证明 d p dp dp的转移具有决策单调性

实现:
因为 c o s t ( j , i ) cost(j,i) cost(j,i)不好快速计算,不适合用二分栈来做,所以这题是用分治实现的
Code

[SDOI2016]征途

高中数学告诉我们: s 2 = x 2 ‾ − x ‾ 2 s^2=overline {x^2}-{overline x}^2 s2=x2x2
所以 m 2 s 2 = m ∑ v i 2 − ( ∑ v i ) 2 m^2s^2=msum v_i^2-(sum v_i)^2 m2s2=mvi2(vi)2
− ( ∑ v i ) 2 -(sum v_i)^2 (vi)2为定值,我们只要令 ∑ v i 2 sum v_i^2 vi2最小即可
f [ i ] [ j ] f[i][j] f[i][j]表示把前 i i i段路分为 j j j天走, ∑ k = 1 j v k 2 sum_{k=1}^{j}v_k^2 k=1jvk2的最小值
s i s_i si表示前 i i i段路的长度和
f [ i ] [ j ] f[i][j] f[i][j]有转移式: f [ i ] [ j ] = m i n { f [ x ] [ j − 1 ] + ( s i − s x ) 2 } f[i][j]=min{f[x][j-1]+(s_i-s_x)^2} f[i][j]=min{f[x][j1]+(sisx)2}

斜率优化:
f [ i ] [ j ] = f [ x ] [ j − 1 ] + ( s i − s x ) 2 f[i][j]=f[x][j-1]+(s_i-s_x)^2 f[i][j]=f[x][j1]+(sisx)2
   ⟺    2 s i s x + f [ i ] [ j ] − s i 2 = f [ x ] [ j − 1 ] + s x 2 iff 2s_is_x+f[i][j]-s_i^2=f[x][j-1]+s_x^2 2sisx+f[i][j]si2=f[x][j1]+sx2
2 s i 2s_i 2si看成 k k k s x s_x sx看成 x x x f [ i ] [ j ] − s i 2 f[i][j]-s_i^2 f[i][j]si2看成 b b b f [ x ] [ j − 1 ] + s x 2 f[x][j-1]+s_x^2 f[x][j1]+sx2看成 y y y
找最优决策点即找到点 ( s x , f [ x ] [ j − 1 ] + s x 2 ) (s_x,f[x][j-1]+s_x^2) (sx,f[x][j1]+sx2)使得 b b b最小
Code

[HNOI2011]数学作业

容易得到递推式: f [ n ] = f [ n − 1 ] × 1 0 l e n ( n ) + n m o d    m f[n]=f[n-1]times 10^{len(n)}+n mod m f[n]=f[n1]×10len(n)+nmodm
构造转移矩阵:
[ f [ n ] n 1 ] = [ f [ n − 1 ] n − 1 1 ] × [ 1 0 l e n ( n ) 0 0 1 1 0 1 1 1 ] begin{bmatrix} f[n]&n&1end{bmatrix}=begin{bmatrix}f[n-1]&n-1&1end{bmatrix}timesbegin{bmatrix}10^{len(n)}&0&0\1&1&0\1&1&1end{bmatrix} [f[n]n1]=[f[n1]n11]× 10len(n)11011001
如果不是 1 0 l e n ( n ) 10^{len(n)} 10len(n)这个奇怪的变量,这道题已经做完了
那我们干脆把 1 0 l e n ( n ) 10^{len(n)} 10len(n)变成常量:枚举位数,对每一个位数做一次矩阵快速幂

[USACO07NOV]Cow Relays G

经典结论:
用矩阵 A A A表示有向图 G G G的连通情况( A i , j = 1 A_{i,j}=1 Ai,j=1表示 G G G中存在边 i → j ito j ij
那么从 s s s t t t经过 k k k条边的路径数= ( A k ) s , t (A^k)_{s,t} (Ak)s,t

拓展到本题:
我们定义一种新运算 ∗ *
[ a 1 , 1 a 1 , 2 ⋯ a 1 , m a 2 , 1 a 2 , 2 ⋯ a 2 , m ⋮ ⋮ ⋱ ⋮ a n , 1 a n , 2 ⋯ a n , m ] ∗ [ b 1 , 1 b 1 , 2 ⋯ b 1 , n b 2 , 1 b 2 , 2 ⋯ b 2 , n ⋮ ⋮ ⋱ ⋮ b m , 1 b m , 2 ⋯ b m , n ] = [ c 1 , 1 c 1 , 2 ⋯ c 1 , n c 2 , 1 c 2 , 2 ⋯ c 2 , n ⋮ ⋮ ⋱ ⋮ c n , 1 c n , 2 ⋯ c n , n ] begin{bmatrix} a_{1,1}&a_{1,2}&cdots&a_{1,m}\a_{2,1}&a_{2,2}&cdots&a_{2,m}\vdots&vdots&ddots&vdots\a_{n,1}&a_{n,2}&cdots&a_{n,m}end{bmatrix} * begin{bmatrix} b_{1,1}&b_{1,2}&cdots&b_{1,n}\b_{2,1}&b_{2,2}&cdots&b_{2,n}\vdots&vdots&ddots&vdots\b_{m,1}&b_{m,2}&cdots&b_{m,n}end{bmatrix} =begin{bmatrix} c_{1,1}&c_{1,2}&cdots&c_{1,n}\c_{2,1}&c_{2,2}&cdots&c_{2,n}\vdots&vdots&ddots&vdots\c_{n,1}&c_{n,2}&cdots&c_{n,n}end{bmatrix} a1,1a2,1an,1a1,2a2,2an,2a1,ma2,man,m b1,1b2,1bm,1b1,2b2,2bm,2b1,nb2,nbm,n = c1,1c2,1cn,1c1,2c2,2cn,2c1,nc2,ncn,n
其中 c i , j = M i n k = 1 m { a i , k + b k , j } c_{i,j}=Min_{k=1}^m{}{a_{i,k}+b_{k,j}} ci,j=Mink=1m{ai,k+bk,j}
因为 ∗ * 运算满足结合律,所以 A ∗ A ∗ A ∗ ⋯ ∗ A A*A*A*cdots *A AAAA仍然可以用快速幂求得

在这种新运算下,如果初始矩阵中 A i , j A_{i,j} Ai,j表示 i i i j j j的最小距离
那么从 s s s t t t经过 k k k条边的最短路径长度= ( A k ) s , t (A^k)_{s,t} (Ak)s,t(这里的 A k A^k Ak表示 k k k A A A ∗ *
博客

[SCOI2014]方伯伯的玉米田

首先本题需要确定一个事实:
每一次的拔高操作区间右端点一定是最右边的玉米

f [ i ] [ j ] f[i][j] f[i][j]表示以 i i i 结尾, i i i 这个点已经被 j j j 个拔高区间覆盖过,所能得到的最长不下降序列长度
f [ i ] [ j ] = m a x { f [ k ] [ l ] } + 1 ( 1 ≤ k < i , 0 ≤ l ≤ j , h [ i ] + j ≥ h [ l ] [ k ] f[i][j]=max{f[k][l]}+1(1leqk<i,0leq lleq j,h[i]+jgeq h[l][k] f[i][j]=max{f[k][l]}+1(1<i,0lj,h[i]+jh[l][k]

用数据结构优化转移即可
Code

[NOIP2018 提高组] 保卫王国

博客
Code

最后

以上就是成就香菇为你收集整理的NOIP复健计划——动态规划的全部内容,希望文章能够帮你解决NOIP复健计划——动态规划所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部