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 3N3830,2B<N,0Ui200000

Solution

显然是一个环形模型,考虑使用两次DP法解决。

首先我们先忽略环形模型这一性质,仅考虑线性 1 ∼ n 1\\sim n 1n 的简化问题,即从第 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[i1,j,0],f[i1,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[i1,j1,0],f[i1,j1,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 1n,编号为 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(ij,nij),也就是逆时针或顺时针从 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动态规划:消除后效性

dp(未完成)

dp状态压缩

区间型动态规划的记忆化搜索实现与环形动态规划的循环数组实现

动态规划·题型归纳

算法进阶——贪心与动态规划