码迷,mamicode.com
首页 > 其他好文 > 详细

「区间DP」关路灯

时间:2019-09-11 18:26:45      阅读:66      评论:0      收藏:0      [点我收藏+]

标签:ace   ble   span   前缀   直线   pre   代码   一个   clu   

关路灯

原题链接:关路灯

题目大意

给你一条直线,直线上有\(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」关路灯

标签:ace   ble   span   前缀   直线   pre   代码   一个   clu   

原文地址:https://www.cnblogs.com/Nicoppa/p/11507504.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!