洛谷题单指南-动态规划3-P1220 关路灯

原题链接:https://www.luogu.com.cn/problem/P1220

题意解读:按坐标顺序排列1~n个路灯,每个路灯有不同的功耗,老张从位置c开始关灯,第一时间关掉c位置的灯,每次关掉一个灯之后,可以往右走、也可以往左走关下一个灯,老张速度是1m/s,求所有灯都关掉所消耗的最少功耗。

解题思路:

由题意分析,关闭一个范围内路灯的过程所消耗的功耗,可以由关闭该范围内一部分路灯的消耗推出,大概率是一个区间DP问题。

尝试进行DP问题分析:

1、状态表示

考虑最后一个关闭的路灯,根据题意,最后一个关闭的要么是头,要么是尾,有两种可能

所以预设两个状态:

设f[i][j]表示将第i ~ j的路灯关闭,且最后一个关闭的是i时,所消耗的总功耗;

设g[i][j]表示将第i ~ j的路灯关闭,且最后一个关闭的是j时,所消耗的总功耗;

设a[i]是第i个路灯的坐标,b[i]是第i个路灯的功耗。

2、状态转移

对于f[i][j],由于最后一个关闭的是i,因此可能有两种情况:从i+1到i、从j到i

  f[i][j] = min(f[i+1][j] + 关最后一个路灯的时间内所有亮着的灯的功耗,g[i+1][j] + 关最后一个路灯的时间内所有亮着的灯的功耗)

  对于情况一:f[i+1][j] + 关最后一个路灯的时间所有亮着的灯的功耗

    如何计算“关最后一个路灯的时间内所有亮着的灯的功耗”?

    

    如上图,最后一步,老张从i+1到i,所需时间为a[i+1] - a[i],此时所有未关闭的灯是1~i、j+1~n,总功耗就是时间 * 这些灯的功耗之和 

    如何求1~i、j+1~n的功耗之和?可以借助前缀和!

    设s[i]是b[1]~b[i]的和,即s[]是b[]的前缀和数组。

    则有“关最后一个路灯的时间内所有亮着的灯的功耗” = (a[i+1] - a[i]) * (s[i] + s[n] - s[j])

  对于情况二:g[i][j-1] + 关最后一个路灯的时间内所有亮着的灯的功耗

    如何计算“关最后一个路灯的时间内所有亮着的灯的功耗”?

  

    

    如上图,最后一步,老张从j到i,所需时间为a[j] - a[i],此时所有未关闭的灯是1~i、j+1~n,总功耗就是时间 * 这些灯的功耗之和 

    则有“关最后一个路灯的时间内所有亮着的灯的功耗” = (a[j] - a[i]) * (s[i] + s[n] - s[j])

所以有

f[i][j] = min(f[i+1][j] + (a[i+1] - a[i]) * (s[i] + s[n] - s[j]),g[i+1][j] + (a[j] - a[i]) * (s[i] + s[n] - s[j]))

同样的方式,可以得到

 

如上图,对应f[i][j-1] + (a[j] - a[i]) * (s[i-1] + s[n] - s[j-1])

如上图,对应g[i][j-1] + (a[j] - a[j-1]) * (s[i-1] + s[n] - s[j-1])

所以有

g[i][j] = min(f[i][j-1] + (a[j] - a[i]) * (s[i-1] + s[n] - s[j-1]), g[i][j-1] + (a[j] - a[j-1]) * (s[i-1] + s[n] - s[j-1]))

枚举时,可以先枚举区间长度,任何一次关闭动作要从一个路灯走到另一个路灯,区间长度至少是2,再枚举左端点,计算右端点即可。

3、初始化

f、g初始化为极大值,方便求最小值

对于位置c已经瞬间关闭,因此f[c][c] = g[c][c] = 0

4、结果

min ( f[1][n] , g[1][n] )

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N= 55;
int n, c;
int a[N], b[N], s[N]; //a[i]是第i个路灯的坐标,b[i]是第i个路灯的功耗,s是b的前缀和
int f[N][N]; //f[i][j]表示将第i ~ j的路灯关闭,且最后一个关闭的是i时,所消耗的总功耗
int g[N][N]; //g[i][j]表示将第i ~ j的路灯关闭,且最后一个关闭的是j时,所消耗的总功耗

int main()
{
    cin >> n >> c;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i] >> b[i];
        s[i] = s[i-1] + b[i];
    } 

    memset(f, 0x3f, sizeof(f));
    memset(g, 0x3f, sizeof(g));
    f[c][c] = g[c][c] = 0; //c路灯瞬间关闭,不消耗时间
    for(int len = 2; len <= n; len++)
    {
        for(int i = 1; i + len - 1 <= n; i++)
        {
            int j = i + len - 1;
            f[i][j] = min(f[i+1][j] + (a[i+1] - a[i]) * (s[i] + s[n] - s[j]), g[i+1][j] + (a[j] - a[i]) * (s[i] + s[n] - s[j]));
            g[i][j] = min(f[i][j-1] + (a[j] - a[i]) * (s[i-1] + s[n] - s[j-1]), g[i][j-1] + (a[j] - a[j-1]) * (s[i-1] + s[n] - s[j-1]));
        }
    }
    cout << min(f[1][n], g[1][n]);

    return 0;
}

 

posted @ 2024-05-15 18:32  五月江城  阅读(47)  评论(0编辑  收藏  举报