Vijos-p1150(动态规划+区间dp)

关路灯

描述

某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少)。老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯。

为了给村里节省电费,老张记录下了每盏路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能够最节省电。他每天都是在天亮时首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯。开始他以为先算一下左边路灯的总功率再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头来关掉另一边的路灯,而事实并非如此,因为在关的过程中适当地调头有可能会更省一些。

现在已知老张走的速度为1m/s,每个路灯的位置(是一个整数,即距路线起点的距离,单位:m)、功率(W),老张关灯所用的时间很短而可以忽略不计。

请你为老张编一程序来安排关灯的顺序,使从老张开始关灯时刻算起所有灯消耗电最少(灯关掉后便不再消耗电了)。

格式

输入格式

第一行是两个数字n(0<n<50,表示路灯的总数)和c(1<=c<=n老张所处位置的路灯号);
接下来n行,每行两个数据,表示第1盏到第n盏路灯的位置和功率。

输出格式

一个数据,即最少的功耗(单位:J,1J=1W·s)。

样例1

样例输入1

5 3 
2 10
3 20
5 20
6 30
8 10

样例输出1

270

限制

每个测试点1s

提示

此时关灯顺序为3 4 2 1 5

解题

传送门
直接参考大佬思路了:

/*
经典好题~一个dp必学题叭~~
首先我们要想怎么样才能完整表示一个状态~
我们可以看到的
如果去关灯关掉了区间[l,r]的灯
那么最后关的一盏不是l就是r
所以我们可以想到用这样一个状态表示
f[l][r][k]表示关掉区间[l,r]的所有灯所需要的最小代价
其中k=0表示现在人在l即最后关的灯是l
k=1表示现在人在r即最后关的是r这盏灯
这样就设计出了一个正确无误的状态
先考虑初值
f[i][i]就是从起始位置直接到i开i灯的代价
那么我们来考虑状态转移
我们知道f[l][r][0]表示的是最后关掉了l这盏灯使得[l,r]全部都关了
那么对应的上一个状态必然是已经关掉了[l+1,r]区间所有的灯
那么我们就要考虑到到底是f[l+1][r][0]转移过来更优还是f[l+1][r][1]更优
即考虑到了左右两种完全的情况
那么很容易有状态转移
f[i][j][0]=min(f[i+1][j][0]+(sump-(s[i+1][j]))*getd(i,i+1),
               f[i+1][j][1]+(sump-(s[i+1][j]))*getd(i,j));
其中我们已经预处理出sump表示所有灯总功率
s[i]表示1...i的所有灯的功率然后用前缀和可以算出s[i+1][j]
这个转移仔细理解一下就好了
同理就可以得出f[i][j][1]的转移
递推的时候外层循环枚举长度内层循环枚举起点
最后取f[1][n][0]和f[1][n][1]的更优值就好了~
时间复杂度O(n^2)
可以直接秒杀~
*/

AC代码

#include <algorithm>  //关路灯
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 52;
int n, c;
int x[maxn], w[maxn], sump;
int dp[maxn][maxn][2], s[maxn];
// dp[i][j] 选择区间[i, j]时, 最小消耗
// time[i][j] 表示从i到j的时间
// s[i]功率前缀和

int getd(int a, int b) {
    
     return abs(x[b] - x[a]); }

void DP() {
    
    
    memset(dp, 0x3f, sizeof(dp));
    for (int i = 1; i <= n; i++)  //区间长度为1时
        dp[i][i][0] = dp[i][i][1] = abs(x[i] - x[c]) * sump;

    for (int l = 2; l <= n; l++) {
    
    
        for (int i = 1; i + l - 1 <= n; i++) {
    
    
            int j = i + l - 1;
            dp[i][j][0] =
                min(dp[i + 1][j][0] + (sump - (s[j] - s[i])) * getd(i, i + 1),
                    dp[i + 1][j][1] + (sump - (s[j] - s[i])) * getd(i, j));
            // s[j] - s[i] 表示区间[i + 1, j]的功率和
            dp[i][j][1] = min(
                dp[i][j - 1][0] + (sump - (s[j - 1] - s[i - 1])) * getd(i, j),
                dp[i][j - 1][1] + (sump - (s[j - 1] - s[i - 1])) * getd(j - 1, j));
        }
    }
    cout << min(dp[1][n][0], dp[1][n][1]) << endl;
}

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

    // s[i][j] = s[j] - s[i - 1];
    DP();

    system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45349225/article/details/109551535