关路灯_洛谷1220_dp

本文介绍了一个关于路灯关闭顺序的问题,旨在找到一种策略使得从开始关灯到所有灯关闭期间消耗的总电量最少。通过动态规划的方法,实现了计算最优路径的目标。

题目描述


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

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

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

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

输入格式:


文件第一行是两个数字n(0

输出格式:


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

Solution’


这题好劲a,1A大法好

首先读懂题目,这位大爷显然是只能关与当前位置相邻的灯的,那么就又变成了一段数字从两端取的模型,不同的是这是从中间往两边罢了

注意到题目的不同,我们的方程也要改。f[i][j][0] 表示i至j之间的灯都关上了,且当前大爷在i的位置上,f[i][j][1] 表示关灯后我们的大爷在j的位置
一开始的想法是既然要找最小功耗,那么是不是可以变成最大节省的功?显然是不行的,因为时间是未知的。我们可以用总的功率减去已经关上的灯i至j的功率计算功耗

还有就是,我们的大爷足够聪明,即他不会从最左边走去关最右边的灯再走回最左边,也不会从最右边走向最左边关掉灯然后走回去,也就是f[i][j][0]只能从f[i+1][j][01]转移,另一边同理

转移显然

f[i][j][0]=min(f[i+1][j][0]+dis(i,i+1)t=i+1t<=jp[t],f[i+1][j][1]+dis(i,j)t=i+1t<=jp[t])

f[i][j][1]=min(f[i][j1][0]+dis(i,j)t=it<=j1p[t],f[i][j1][1]+dis(j1,j)t=i+1t<=j1p[t])

其中dis(i,j)表示路灯间的距离

Code


#include <stdio.h>
#include <string.h>
#include <math.h>
#define rep(i, a, b) for (int i = a; i <= b; i ++)
#define fill(x, t) memset(x, t ,sizeof(x))
#define N 101
using namespace std;
int f[N][N][2], p[N], dis[N], sum[N], t[N];
inline int read(){
    int x = 0, v = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-'){
            v = -1;
        }
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9'){
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * v;
}
inline int min(int x, int y){
    return x<y?x:y;
}
inline int cal(int i, int j){
    return fabs(sum[j] - sum[i - 1]);
}
inline int dist(int i, int j){
    return fabs(dis[j] - dis[i]);
}
int main(void){
    int n = read(), st = read();
    rep(i, 1, n){
        t[i] = read();
        p[i] = read();
        dis[i] = dis[i - 1] + t[i] - t[i - 1];
        sum[i] = sum[i - 1] + p[i];
    }
    fill(f, 63);
    f[st][st][0] = f[st][st][1] = 0;
    for (int i = st; i >= 1; i --){
        for (int j = st; j <= n; j ++){
            f[i][j][0] = min(f[i][j][0], f[i + 1][j][0] + dist(i + 1, i) * (sum[n] - cal(i + 1, j)));
            f[i][j][0] = min(f[i][j][0], f[i + 1][j][1] + dist(j, i) * (sum[n] - cal(i + 1, j)));
            f[i][j][1] = min(f[i][j][1], f[i][j - 1][0] + dist(j, i) * (sum[n] - cal(i, j - 1)));
            f[i][j][1] = min(f[i][j][1], f[i][j - 1][1] + dist(j, j - 1) * (sum[n] - cal(i, j - 1)));
        }
    }
    printf("%d\n", min(f[1][n][0], f[1][n][1]));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值