洛谷P1220 关路灯
? 解题记录 ? ? 洛谷 ? ? 区间dp ? ? 动态规划 ?    2017-09-26 23:41:27    372    0    0

 

题目描述

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

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

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

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

输入输出格式

输入格式:

 

文件第一行是两个数字n(0<n<50,表示路灯的总数)和c(1<=c<=n老张所处位置的路灯号);

接下来n行,每行两个数据,表示第1盏到第n盏路灯的位置和功率。

 

输出格式:

 

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

 

输入输出样例

输入样例#1:
5 3
2 10
3 20
5 20
6 30
8 10
输出样例#1:
270

说明

输出解释:

{此时关灯顺序为3 4 2 1 5,不必输出这个关灯顺序}

又一道全凭自己脑补的DP,高兴ing。DP入门指日可待。

这一道题乍一看很诡异,像是搜索。但是不难发现经过一盏路灯不关一定不是最优的,如果进一步仔细观察就会发现如果包含起点的一段路灯全部被关完的最佳方案知道,那么可以通过关左边的、右边的来推出更大的区域的最佳方案。眼熟吗?对了,就是区间dp嘛。

那么,首先我们对路灯按照位置排序。然后用dp[i][j][0/1]表示i,j之间的闭区间的灯全部关闭,且人在左/右至少需要耗电多少。不难发现,这个状态的设计是满足无后效性、最优子结构的。最后的问题是如何求效率,考虑只有n = 50,可以做一遍前缀和。那么我们就可以写状态转移方程了:dp[j][j + i][0] = min(dp[j + 1][j + i][0] + (sum[1][j] + sum[j + i + 1][n]) * (lamp[j + 1].pos - lamp[j].pos), dp[j + 1][j + i][1] + (sum[1][j] + sum[j + i + 1][n]) * (lamp[j + i].pos - lamp[j].pos));

dp[j][j + i][1] = min(dp[j][j + i - 1][0] + (sum[1][j - 1] + sum[j + i][n]) * (lamp[j + i].pos - lamp[j].pos), dp[j][j + i - 1][1] + (sum[1][j - 1] + sum[j + i][n]) * (lamp[j + i].pos - lamp[j + i - 1].pos));

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[170][170][2], sum[170][170], n, c, st;
struct pir {
    int pos, p;
    bool operator < (const pir & a) const{
        return pos < a.pos;
    }
}lamp[170];
int main() {
    scanf("%d%d", &n, &c);
    for(register int i = 1; i <= n; ++i) 
        scanf("%d%d", &lamp[i].pos, &lamp[i].p);
    sort(lamp + 1, lamp + n + 1);
    for(register int i = 1; i <= n; ++i) {
        if(lamp[i].pos == c) {st = i;break;}
    }
    for(register int i = 1; i <= n; ++i) {
        sum[i][i] = lamp[i].p;
        for(register int j = i + 1; j <= n; ++j)
            sum[i][j] = sum[i][j - 1] + lamp[j].p;
    }
    memset(dp, 0x3f, sizeof(dp));
    dp[c][c][0] = dp[c][c][1] = 0;
    for(register int i = 1; i < n; ++i)
        for(register int j = c - i; j <= c; ++j) {
            if(j <= 0) continue;
            dp[j][j + i][0] = min(dp[j + 1][j + i][0] + (sum[1][j] + sum[j + i + 1][n]) * (lamp[j + 1].pos - lamp[j].pos), dp[j + 1][j + i][1] + (sum[1][j] + sum[j + i + 1][n]) * (lamp[j + i].pos - lamp[j].pos));
            dp[j][j + i][1] = min(dp[j][j + i - 1][0] + (sum[1][j - 1] + sum[j + i][n]) * (lamp[j + i].pos - lamp[j].pos), dp[j][j + i - 1][1] + (sum[1][j - 1] + sum[j + i][n]) * (lamp[j + i].pos - lamp[j + i - 1].pos));
        }
    printf("%d", min(dp[1][n][0], dp[1][n][1]));
    return 0;
} 


上一篇: 洛谷P1282 多米诺骨牌

下一篇: 洛谷P1373 小a和uim之大逃离

372 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航