0x55. 动态规划 - 环形与后效性处理(例题详解 × 6)
Posted 繁凡さん
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了0x55. 动态规划 - 环形与后效性处理(例题详解 × 6)相关的知识,希望对你有一定的参考价值。
目录
本系列博客是《算法竞赛进阶指南》的学习笔记,包含书中的部分重要知识点、例题解题报告及我个人的学习心得和对该算法的补充拓展,仅用于学习交流和复习,无任何商业用途。博客中部分内容来源于书本和网络 ,由我个人整理总结。部分内容由我个人编写而成,如果想要有更好的学习体验或者希望学习到更全面的知识,请于京东搜索购买正版图书:《算法竞赛进阶指南》——作者李煜东,强烈安利,好书不火系列,谢谢配合。
%
学习笔记目录链接: 学习笔记目录链接
%
整理的算法模板合集: ACM模板
%
点我看算法全家桶系列!!!
文章目录
0x55.1 环形结构上的动态规划问题
两次DP法
两次DP法,就是执行两次DP过程,第一次在任意位置将环断开成链,按照线性结构求解。第二次通过适当的条件和赋值,强制新增状态,保证计算出的状态等价于把断开的位置强制相连,两次DP的答案取最优值即可。
Problem A. Naptime
POJ2228 / AcWing 288. 休息时间
在某个星球上,一天由 n n n 个小时构成,我们称 0 0 0 点到 1 1 1 点为第 1 1 1 个小时、 1 1 1 点到 2 2 2 点为第 2 2 2 个小时,以此类推。
在第 i i i 个小时睡觉能够恢复 U i U_i Ui 点体力。
在这个星球上住着一头牛,它每天要休息 m m m 个小时。
它休息的这 m m m 个小时不一定连续,可以分成若干段,但是在每段的第一个小时,它需要从清醒逐渐入睡,不能恢复体力,从下一个小时开始才能睡着。
为了身体健康,这头牛希望遵循生物钟,每天采用相同的睡觉计划。
另外,因为时间是连续的,即每一天的第 n n n 个小时和下一天的第 1 1 1 个小时是相连的( n n n 点等于 0 0 0 点),这头牛只需要在每 n n n 个小时内休息够 m m m 个小时就可以了。
请你帮忙给这头牛安排一个睡觉计划,使它每天恢复的体力最多。
3 ≤ N ≤ 3830 , 2 ≤ B < N , 0 ≤ U i ≤ 200000 3≤N≤3830, 2≤B<N, 0≤U_i≤200000 3≤N≤3830,2≤B<N,0≤Ui≤200000
Solution
显然是一个环形模型,考虑使用两次DP法解决。
首先我们先忽略环形模型这一性质,仅考虑线性 1 ∼ n 1\\sim n 1∼n 的简化问题,即从第 1 1 1 小时处将环断开,转换为线性结构考虑如何解决该线性DP。
由于每次休息第一小时并不能增加体力值,所以我们需要维护当前是否在休息,所以设 f [ i , j , 0 / 1 ] f[i,j,0/1] f[i,j,0/1] 表示前 i i i 小时中有 j j j 小时在休息,第 i i i 小时 在 / 不在 休息,能恢复的最大体力值。
显然有转移方程:
f
[
i
,
j
,
0
]
=
max
{
f
[
i
−
1
,
j
,
0
]
,
f
[
i
−
1
,
j
,
1
]
}
f[i,j,0]=\\max \\{ f[i−1,j,0],f[i−1,j,1] \\}
f[i,j,0]=max{f[i−1,j,0],f[i−1,j,1]}
f
[
i
,
j
,
1
]
=
max
{
f
[
i
−
1
,
j
−
1
,
0
]
,
f
[
i
−
1
,
j
−
1
,
1
+
U
i
]
}
f[i,j,1]=\\max \\{ f[i−1,j−1,0],f[i−1,j−1,1+U_i] \\}
f[i,j,1]=max{f[i−1,j−1,0],f[i−1,j−1,1+Ui]}
初始化:
f
[
1
,
0
,
0
]
=
f
[
1
,
1
,
1
]
=
0
f[1,0,0] = f[1,1,1] = 0
f[1,0,0]=f[1,1,1]=0,其余负无穷。(简化线性问题第一小时不可能处于熟睡状态)
目标:
max
f
[
n
,
m
,
0
]
,
f
[
n
,
m
,
1
]
\\max{f[n,m,0],f[n,m,1]}
maxf[n,m,0],f[n,m,1]
考虑将线性结构恢复到环形结构需要增加的状态。发现如果是一个环形结构,第一个小时有可能处于熟睡状态,可以加上 U 1 U_1 U1,因此我们只需要增加这一状态,重新DP一次取最大值即可,即强制令第 n n n 个小时和第 1 1 1 个小时均在休息,令 f [ 1 , 1 , 1 ] = U 1 f[1,1,1]=U_1 f[1,1,1]=U1,其余均为负无穷,求出的答案 f [ n , m , 1 ] f[n,m,1] f[n,m,1] 与上面线性结构取最值即可。
空间限制 64MB,计算之后发现需要 O ( n 2 ) = n 2 × 2 = 111.9 O(n^2)=n^2\\times 2=111.9 O(n2)=n2×2=111.9 MB,空间显然不够,考虑滚动数组优化至 O ( n ) O(n) O(n) 即可。
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7, INF = 0x3f3f3f3f;
int n, m, s, t, k, ans;
int u[maxn];
int f[2][maxn][2];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; ++ i)
scanf("%d", &u[i]);
if(m == 0) return 0 * puts("0");
memset(f, 0xcf, sizeof f);
f[1 & 1][0][0] = f[1 & 1][1][1] = 0;
for (int i = 2; i <= n; ++ i) {
for (int j = 0; j <= m; ++ j) {
f[i & 1][j][0] = max(f[i - 1 & 1][j][0], f[i - 1 & 1][j][1]);
if(j - 1 >= 0)
f[i & 1][j][1] = max(f[i - 1 & 1][j - 1][0], f[i - 1 & 1][j - 1][1] + u[i]);
}
}
ans = max(f[n & 1][m][0], f[n & 1][m][1]);
memset(f, 0xcf, sizeof f);
f[1][1][1] = u[1];
for (int i = 2; i <= n; ++ i) {
for (int j = 0; j <= m; ++ j) {
f[i & 1][j][0] = max(f[i - 1 & 1][j][0], f[i - 1 & 1][j][1]);
if(j - 1 >= 0)
f[i & 1][j][1] = max(f[i - 1 & 1][j - 1][0], f[i - 1 & 1][j - 1][1] + u[i]);
}
}
ans = max(ans, f[n & 1][m][1]);
cout << ans << endl;
return 0;
}
破环成链法
Problem B. 环路运输
AcWing 289
在一条环形公路旁均匀地分布着 n n n 座仓库,编号为 1 ∼ n 1\\sim n 1∼n,编号为 i i i 的仓库与编号为 j j j 的仓库之间的距离定义为 d i s t ( i , j ) = min ( ∣ i − j ∣ , n − ∣ i − j ∣ ) \\mathrm{dist}(i,j)=\\min(|i−j|,n−|i−j|) dist(i,j)=min(∣i−j∣,n−∣i−j∣),也就是逆时针或顺时针从 i i i 到 j j j 中较近的一种。
每座仓库都存有货物,其中编号为 i i i 的仓库库存量为 A i A_i Ai。
在 i i i 和 j j j 两座仓库之间运送货物需要的代价为 A i + A j + d i s t ( i , j ) A_i+A_j+\\mathrm {dist}(i,j) Ai+Aj动态规划:消除后效性