HLOJ434 关灯

51 篇文章 0 订阅
6 篇文章 0 订阅

题目

题目描述
宁智贤得到了一份有趣而高薪的工作。每天早晨她必须关掉她所在村庄的街灯。所有的街灯都被设置在一条直路的同一侧。 宁智贤每晚到早晨5点钟都在晚会上,然后她开始关灯。开始时,她站在某一盏路灯的旁边。 每盏灯都有一个给定功率的电灯泡,因为宁智贤有着自觉的节能意识,她希望在耗能总数最少的情况下将所有的灯关掉。 宁智贤因为太累了,所以只能以1m/s的速度行走。关灯不需要花费额外的时间,因为当她通过时就能将灯关掉。 编写程序,计算在给定路灯设置,灯泡功率以及宁智贤的起始位置的情况下关掉所有的灯需耗费的最小能量。
输入格式
第一行包含一个整数N,2≤N≤1000,表示该村庄路灯的数量。
第二行包含一个整数V,1≤V≤N,表示宁智贤开始关灯的路灯号码。
接下来的N行中,每行包含两个用空格隔开的整数D和W,用来描述每盏灯的参数,其中0≤D≤1000,0≤W≤1000。
D表示该路灯与村庄开始处的距离(用米为单位来表示),W表示灯泡的功率,即在每秒种该灯泡所消耗的能量数。路灯是按顺序给定的。
输出格式
第一行即唯一的一行应包含一个整数,即消耗能量之和的最小值。

题解

说实话,这道区间DP拿到手想了四十分钟一点思路都没有……看的题解才写出来的。。
这道题目要做的第一步是把所有的灯按照坐标进行排序
然后设出状态 f[i][j] f [ i ] [ j ] 代表把第 i i 盏灯到第j栈灯全部关闭的最小消耗。
但是问题出现了,我们发现对于一个区间 [i,j] [ i , j ] ,我们从 i i 走到j的消耗和从 j j 走到i的消耗是不一样的,所以我们新开一维, f[i][j][1] f [ i ] [ j ] [ 1 ] 代表从左走到右边, f[i][j][0] f [ i ] [ j ] [ 0 ] 代表从右走到左边。
对于 f[i][j] f [ i ] [ j ] ,我们只能从左边或者右边递推而来,也就是从 f[i][j1] f [ i ] [ j − 1 ] 或者 f[i+1][j] f [ i + 1 ] [ j ] 推来。

code

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int num = 0;
    char c = ' ';
    bool flag = true;
    for(;c > '9' || c < '0';c = getchar())
    if(c == '-')
    flag = false;
    for(;c >= '0' && c <= '9';num = num*10+c-48,c=getchar());
    return flag ? num : -num;
}
const int maxn=1020;
int n,begining,P[maxn],sum[maxn][maxn];
//P是a[].power的前缀和
//sum[i][j]代表除去[i,j]的灯后的功率和 
struct light
{
    int direct,power;
}a[maxn];
bool mycmp(light a,light b)
{
    return a.direct<b.direct;
};
void init()
{
    n=read();
    begining=read();
    for(int i=1;i<=n;i++)
    {
        a[i].direct=read();
        a[i].power=read();
    }
    sort(a+1,a+1+n,mycmp);//排序 
    for(int i=1;i<=n;i++)
        P[i]=P[i-1]+a[i].power;//前缀和 
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            sum[i][j]=P[n]+P[i-1]-P[j];
}
int f[maxn][maxn][2];
void DP()
{
    memset(f,10,sizeof f);
    f[begining][begining][1]=f[begining][begining][0]=0;
    for(int len=2;len<=n;len++)
        for(int i=1;i+len-1<=n;i++)
        {
            int j=i+len-1;
            int t1,t2;
            t1=f[i+1][j][0]+(a[i+1].direct-a[i].direct)*sum[i+1][j];
            t2=f[i+1][j][1]+(a[j].direct-a[i].direct)*sum[i+1][j];
            f[i][j][0]=min(t1,t2);
            t1=f[i][j-1][0]+(a[j].direct-a[i].direct)*sum[i][j-1];
            t2=f[i][j-1][1]+(a[j].direct-a[j-1].direct)*sum[i][j-1];
            f[i][j][1]=min(t1,t2);
            //这部分画个图就能理解了 
        }
    printf("%d\n",min(f[1][n][0],f[1][n][1]));
}
int main()
{
    init();
    DP();
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值