「区间DP」关路灯

Posted nicoppa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「区间DP」关路灯相关的知识,希望对你有一定的参考价值。

关路灯

原题链接:关路灯

题目大意

给你一条直线,直线上有\(n\)个点,每个点每秒都有消耗能量,现在再给你个点\(m\),代表你当前的位置,现在你要去碰这些点,当你碰到这些点时,这些点就不再消耗能量,你的速度是1m/s,现在让你求碰完这些点消耗能量最少,且最少值为多少

题目题解

看了一下题,哎,这题还写了不能用贪心,那就dp咯,还发现每次只能碰一个点,那似乎可以用区间动规\(dp_i, j\)来表示我们目前的状态,代表已经碰了 \(i, j\) 这个区间的点时所消耗的最小能量,用小区间去合成大区间,每次又给你你得停留在哪边,两个状态肯定不够,再设一个状态 \(dp_i, j, k\) 表示走了 \(i, j\) 区间后在哪边。

因为有消耗,为了很快的计算这个消耗,我们可以用前缀和维护,然后套模板就好了?

推导公式如下

太长了 写了半天发现并没有空间解释 我直接写在代码里面吧

//#define fre yes

#include <cstdio>
#include <cstring>
#include <iostream>

const int N = 105;
int place[N], p[N], sum[N];
int f[N][N][2];

int main() 
    static int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) 
        scanf("%d %d", &place[i], &p[i]);
    
    for (int i = 1; i <= n; i++) 
        sum[i] = sum[i - 1] + p[i];
    

    memset(f, 1, sizeof(f));
    f[m][m][0] = f[m][m][1] = 0;
    for (int p = 1; p < n; p++) 
        for (int i = 1, j = i + p; i <= n && j <= n; i++, j++) 
            f[i][j][0] = std::min(f[i + 1][j][0] + (place[i + 1] - place[i]) * (sum[n] - sum[j] + sum[i]), f[i + 1][j][1] + (place[j] - place[i]) * (sum[n] - sum[j] + sum[i]));
            //用小区间更新大区间 
            //place[i + 1] - place[i] -> 意思是走的秒数
            //sum[n] - sum[j] + sum[i] -> 计算能量消耗
            f[i][j][1] = std::min(f[i][j - 1][1] + (place[j] - place[j - 1]) * (sum[n] - sum[j - 1] + sum[i - 1]), f[i][j - 1][0] + (place[j] - place[i]) * (sum[n] - sum[j - 1] + sum[i - 1]));
        
    

    printf("%d\n", std::min(f[1][n][0], f[1][n][1]));
    //最后的状态不知道哪边更小 比一下
    return 0;

以上是关于「区间DP」关路灯的主要内容,如果未能解决你的问题,请参考以下文章

codevs1258 关路灯(☆区间dp)

洛谷P1220 关路灯(区间dp)

区间DP code[vs]1258 关路灯

[luoguP1220] 关路灯(DP)

「LuoguP1220」 关路灯(区间dp

P1220 关路灯 区间DP