OI模板 DP模型 线性/区间/树形

OI模板 DP模型 线性/区间/树形

线性DP

最长上升子序列( O ( n 2 ) O(n^2) O(n2)

F [ i ] = max ⁡ 1 ≤ j < i , a j < a i   { F [ j ] + 1 } F[i] = \max\limits_{1\leq j<i,a_j<a_i}~\{F[j]+1\} F[i]=1j<i,aj<aimax {F[j]+1}

int ans = 1;
for(int i = 1; i <= n; ++ i){
	dp[i] = 1;
	for(int j = 1; j < i; ++ j)
		if(a[j] < a[i])
			dp[i] = max(dp[i], dp[j] + 1);
	ans = max(ans, dp[i]);
}

最长上升子序列( O ( n log ⁡ n ) O(n\log n) O(nlogn)

vector<int> s;
s.push_back(a[1]);
for(int i = 2; i <= n; ++ i){
	if(a[i] > s.back()) s.push_back(a[i]);
	else *lower_bound(s.begin(), s.end(), a[i]) = a[i];
}
ans = s.size();

最长公共子序列

F [ i , j ] = max ⁡ { F [ i − 1 , j ] F [ i , j − 1 ] F [ i − 1 , j − 1 ] + 1      i f   a i = = b j F[i,j] = \max\begin{cases}F[i-1,j] \\ F[i,j-1] \\ F[i-1,j-1]+1~~~~if ~a_i==b_j\end{cases} F[i,j]=maxF[i1,j]F[i,j1]F[i1,j1]+1    if ai==bj

for(int i = 1; i <= n; ++ i)
	for(int j = 1; j <= m; ++ j){
		dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
		if(a[i] == b[j]) dp[i][j] = max(dp[i][j], dp[i-1][j-1] + 1);
	}
ans = dp[n][m];

背包

01背包求最大收益

F [ i , j ] = max ⁡ { F [ i − 1 , j ] F [ i − 1 , j − V i ] + W i    i f   j ≥ V i F[i,j] = \max\begin{cases} F[i-1,j] \\ F[i-1,j-V_i]+W_i~~if~j\geq V_i \end{cases} F[i,j]=max{F[i1,j]F[i1,jVi]+Wi  if jVi

const int N=1;
int n,m,dp[N],v[N],w[N];
//物品数、背包容量、dp数组、物品体积、物品价值
int DP(){
	memset(dp,0xcf,sizeof(dp));
	for(int i=1; i<=n; ++i)
		for(int j=m; j>=v[i]; --j)
			dp[j]=std::max(dp[j],dp[j-v[i]]+w[i]);
	return dp[m];
}

例题:Luogu P1048 [NOIP2005 普及组] 采药

01背包求方案个数

const int N=1;
int n,m,dp[N],v[N];
//物品数、背包容量、dp数组、物品体积
int DP(){
	memset(dp,0,sizeof(dp));
	dp[0]=1;
	for(int i=1; i<=n; ++i)
		for(int j=m; j>=v[i]; --j)
			dp[j]+=dp[j-v[i]];
	return dp[m];
}

例题:Luogu P1164 小A点菜

完全背包求最大收益

const int N=1;
int n,m,dp[N],v[N],w[N];
//物品数、背包容量、dp数组、物品体积、物品价值
int DP(){
	for(int i=1; i<=n; ++i)
		for(int j=v[i]; j<=m; ++j)
			dp[j]=std::max(dp[j],dp[j-v[i]]+w[i]);
	return dp[m];
}

例题:Luogu P1616 疯狂的采药

完全背包求方案个数

与 01背包类似。

例题:Luogu P1832 A+B Problem(再升级)

多重背包直接拆分法

拆分+01背包。

多重背包二进制拆分法

const int N=1;
int n,m,dp[N],v[N],w[N],cnt;
//物品数、背包容量、dp数组、物品体积、物品价值、拆分后物品数
int DP(){
	int v1,w1,c1;
	for(int i=1; i<=n; ++i){//拆分
		scanf("%d%d%d",&v1,&w1,&c1);
		for(int j=1; j<=c1; j<<=1)
			v[++cnt]=j*v1,w[cnt]=j*w1,c1-=j;
		if(c1) v[++cnt]=c1*v1,w[cnt]=c1*w1;
	}
	for(int i=1; i<=cnt; ++i)//01背包
		for(int j=m; j>=w[i]; --j)
			dp[j]=std::max(dp[j],dp[j-w[i]]+v[i]);
	return dp[m];
}

例题:Luogu P1776 宝物筛选

多重背包单调队列优化法

gugugu

分组背包

F [ i , j ] F[i,j] F[i,j]:前 i i i 组选出价值为 j j j 的物品最大收益。

F [ i , j ] = max ⁡ { F [ i − 1 , j ] max ⁡ 1 ≤ k ≤ C i F [ i − 1 , j − V i , k ] + W i , k F[i,j]=\max\begin{cases} F[i-1,j] \\ \max\limits_{1\leq k\leq C_i}F[i-1,j-V_{i,k}]+W_{i,k} \end{cases} F[i,j]=max{F[i1,j]1kCimaxF[i1,jVi,k]+Wi,k

const int N=1010;
int n,m,dp[N],v[N],w[N];
//物品数、背包容量、dp数组、物品体积、物品价值
int g[N][N],c[N],cnt;
//第i组第j个物品编号、第i组物品数、组数
int DP(){
	for(int i=1; i<=cnt; ++i)
		for(int j=m; j>=0; --j)
			for(int k=1; k<=c[i]; ++k)
				if(j>=v[g[i][k]])
					dp[j]=std::max(dp[j],dp[j-v[g[i][k]]]+w[g[i][k]]);
	return dp[m];
}

例题:Luogu P1757 通天之分组背包

区间DP

F ( l , r , s ) F(l,r,s) F(l,r,s) 表示区间 [ l , r ] [l,r] [l,r] s s s 限制下的最优解。

常见转移:

F ( l , r , s ) = max ⁡ l ≤ k < r { F ( l , k , s ) , F ( k + 1 , r , s ) } F(l,r,s) = \max\limits_{l \leq k < r}\{F(l,k,s), F(k+1,r,s)\} F(l,r,s)=lk<rmax{F(l,k,s),F(k+1,r,s)}

F ( l , r , s ) = max ⁡ { F ( l + 1 , r , s ) , F ( l , r − 1 , s ) } F(l,r,s) = \max\{F(l+1,r,s), F(l,r-1,s)\} F(l,r,s)=max{F(l+1,r,s),F(l,r1,s)}

树形DP

最大权独立集问题

F [ x , 0 ] = ∑ s ∈ S o n ( x ) max ⁡ ( F [ s , 0 ] , F [ s , 1 ] ) F[x,0]=\displaystyle\sum\limits_{s\in Son(x)} \max(F[s,0],F[s,1]) F[x,0]=sSon(x)max(F[s,0],F[s,1])

F [ x , 1 ] = W e i x + ∑ s ∈ S o n ( x ) F [ s , 0 ] F[x,1]=Wei_x+\displaystyle\sum\limits_{s\in Son(x)} F[s,0] F[x,1]=Weix+sSon(x)F[s,0]

a n s = max ⁡ ( F [ x , 0 ] , F [ x , 1 ] ) ans = \max(F[x,0],F[x,1]) ans=max(F[x,0],F[x,1])

const int N = 10010;
int n, inD[N], Wei[N], dp[N][2], rt;//节点数、入度、权值、DP数组、根
vector<int> Edge[N];//图
void dfs(int x){
	dp[x][0] = 0, dp[x][1] = Wei[x];
	for(int i = 0; i < Edge[x].size(); ++ i){
		int y = Edge[x][i];
		dfs(y);
		dp[x][0] += max(dp[y][0], dp[y][1]);
		dp[x][1] += dp[y][0];
	}
	return ;
}

//main()函数:
dfs(rt);
ans = max(dp[rt][0], dp[rt][1]);

例题:Luogu P1352 没有上司的舞会

最小权覆盖集问题

F [ x , 0 ] = ∑ s ∈ S o n ( x ) F [ s , 1 ] F[x,0]=\displaystyle\sum\limits_{s\in Son(x)} F[s,1] F[x,0]=sSon(x)F[s,1]

F [ x , 1 ] = ∑ s ∈ S o n ( x ) min ⁡ ( F [ s , 0 ] , F [ s , 1 ] ) F[x,1]=\displaystyle\sum\limits_{s\in Son(x)} \min(F[s,0],F[s,1]) F[x,1]=sSon(x)min(F[s,0],F[s,1])

a n s = min ⁡ ( F [ x , 0 ] , F [ x , 1 ] ) ans = \min(F[x,0],F[x,1]) ans=min(F[x,0],F[x,1])

const int N = 10010;
vector<int> Edge[N];//图
int n, dp[N][2];//节点数、DP数组
void dfs(int x, int fa){
	dp[x][1] = 1, dp[x][0] = 0;
	for(int i = 0; i < Edge[x].size(); ++ i){
		int y = Edge[x][i];
		if(y == fa) continue;
		dfs(y, x);
		dp[x][0] += dp[y][1];
		dp[x][1] += min(dp[y][0], dp[y][1]);
	}
	return ;
}

//main()函数:
dfs(1, 0);
ans = min(dp[1][0], dp[1][1]);

例题:Luogu P2016 战略游戏

树形依赖背包

F [ x , t ] F[x,t] F[x,t]:在以 x x x 为根的子树中选 t t t 个物品的最大收益。

技巧:如有多个节点为根,新建超级根 0 0 0

F [ x , t ] = max ⁡ ∑ i = 1 p   c i = t − 1 { ∑ i = 1 p F [ y i , c i ] } + W e i x F[x,t]=\max\limits_{\sum_{i=1}^p~c_i=t-1} \bigg\{\displaystyle\sum\limits_{i=1}^pF[y_i,c_i]\bigg\}+Wei_x F[x,t]=i=1p ci=t1max{i=1pF[yi,ci]}+Weix

const int N = 310;
int n, m, Wei[N], dp[N][N];//总物品数、选择物品数、物品重量、DP数组
vector<int> Edge[N];//图
void dfs(int x){
	dp[x][0] = 0;
	for(int i = 0; i < Edge[x].size(); ++ i){
		int y = Edge[x][i];
		dfs(y);
		for(int t = m; t >= 0; -- t)//当前背包体积
			for(int j = 0; j <= t; ++ j)//组内物品
				dp[x][t] = max(dp[x][t], dp[x][t-j] + dp[y][j]);
	}
	if(x)//超级根 0 不用占物品数
		for(int t = m; t > 0; -- t)
			dp[x][t] = dp[x][t-1] + Wei[x];
	return ;
}

例题:Luogu P2014 [CTSC1997]选课

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值