zoukankan      html  css  js  c++  java
  • P1220 关路灯

    题目描述

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

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

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

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

    输入输出格式

    输入格式:

    文件第一行是两个数字n(1<=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,不必输出这个关灯顺序}

    Solution:

      本题是一道典型的区间$DP$问题。

      首先考虑如何定义状态,很容易想到定义$f[i][j]$表示关掉$i-j$的路灯浪费的最小总功率。

      但是由于题目中可以回走,关掉$i-j$的灯后,并不知道当前在$i,j$中的哪一点。

      于是再加一维状态,将状态定义为$f[i][j][k],;i,jin [1,n],;kin [0;or;1]$,其中$k=0$表示关掉$i-j$的灯后在$i$点,$k=1$表示关掉$i-j$的灯后在$j$点。

      再来考虑状态转移,为保证从局部最优扩展到整体最优,于是由小区间更新大区间,所以当前$f[i][j][0]$只与$f[i+1][j][0]$和$f[i+1][j][1]$有关,同理$f[i][j][1]$只与$f[i][j-1][1]$和$f[1][j-1][0]$有关。

      但是转移时还要知道没有关闭的灯的功率消耗,于是令前缀和$s_i$表示前$i$个灯的总功率,则除开$i-j$的灯,其余的灯的消耗为$s_i+(s_n-s_j)$,其中$s_i$表示前$i$盏灯的功率消耗,$s_n-s_j$表示$j$后面的灯的功率消耗。

      不难想到状态转移方程:

      $left{egin{matrix}
     f[i][j][0]=min(f[i+1][j][0]+(a[i+1].pos-a[i].pos)*(s[i]+s[n]-s[j]),f[i+1][j][1]+(a[j].pos-a[i].pos)*(s[i]+s[n]-s[j]))\
     quad ;f[i][j][1]=min(f[i][j-1][0]+(a[j].pos-a[i].pos)*(s[i-1]+s[n]-s[j-1]),f[i][j-1][1]+(a[j].pos-a[j-1].pos)*(s[i-1]+s[n]-s[j-1]))\  
    end{matrix} ight.$

      最后不要忘了初始化,很简单,由于要取最小值,所以先将$f$初值赋为无穷大,再考虑状态的定义和给出的出发点$c$,则$f[c][c][0]=f[c][c][1]=0$(表示在$c$点直接关掉第$c$盏灯,不需要耗费时间瞬时功率为$0$,停在了$c$点)。

      实现时,最外层枚举区间长度($kin [1,n]$),内层枚举区间起始点($iin [1,n]$),注意判断区间是否越界,时间复杂度$O(n^2)$。

      输出目标状态$min(f[1][n][0],f[1][n][1])$(由于不知道关完$1-n$盏灯后停在$1$点还是$n$点,所以取最小值)

    代码:

    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    using namespace std;
    int n,c,f[55][55][2],s[55];
    struct point{int pos,w;}a[55];
    int main()
    {
        ios::sync_with_stdio(0);
        cin>>n>>c;
        for(int i=1;i<=n;i++)cin>>a[i].pos>>a[i].w,s[i]=s[i-1]+a[i].w;
        memset(f,0x3f,sizeof(f));
        f[c][c][0]=f[c][c][1]=0;
        for(int k=1;k<=n;k++)
            for(int i=1;i+k<=n;i++){
                int j=i+k;
                f[i][j][0]=min(f[i+1][j][0]+(a[i+1].pos-a[i].pos)*(s[i]+s[n]-s[j]),f[i+1][j][1]+(a[j].pos-a[i].pos)*(s[i]+s[n]-s[j]));
                f[i][j][1]=min(f[i][j-1][0]+(a[j].pos-a[i].pos)*(s[i-1]+s[n]-s[j-1]),f[i][j-1][1]+(a[j].pos-a[j-1].pos)*(s[i-1]+s[n]-s[j-1]));
            }
        cout<<(min(f[1][n][0],f[1][n][1]));
        return 0;
    }
  • 相关阅读:
    LeetCode 83. Remove Duplicates from Sorted List (从有序链表中去除重复项)
    LeetCode 21. Merge Two Sorted Lists (合并两个有序链表)
    LeetCode 720. Longest Word in Dictionary (字典里最长的单词)
    LeetCode 690. Employee Importance (职员的重要值)
    LeetCode 645. Set Mismatch (集合不匹配)
    LeetCode 500. Keyboard Row (键盘行)
    LeetCode 463. Island Perimeter (岛的周长)
    115.Distinct Subsequences
    55.Jump Game
    124.Binary Tree Maximum Path Sum
  • 原文地址:https://www.cnblogs.com/five20/p/8893816.html
Copyright © 2011-2022 走看看