zoukankan      html  css  js  c++  java
  • [題解] luogu p1220 關路燈

    區間dp

    题目描述

    某一村庄在一条路线上安装了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,不必输出这个关灯顺序}


    看題解仍然看不懂,結果在dalao解釋后才看懂轉移方程:

    用 f [ i ] [ j ] [ 0/1 ]表示從 i 號路燈到 j 號路燈,人在區間 左/右 端點時的最小花費。

    f [ i ] [ j ] [ 0 ]有兩種轉移方式:從i+1的左端點往左走一個關 i 號路燈,或者從 i+1,j 的右端點走到左端來關 i 路燈。

    同理 f [ i ] [ j ] [ 1 ]也就類似的知道了。

    用前綴和來計算 i 到 j 區間以外的花費,乘上距離就是這段轉移用的花費。

    枚舉區間長度和左端點即可,然而並不知道為什麼不用枚舉斷開的點,可能是因為狀態轉移不需要兩個區間合併吧。

    這狀態轉移方程到底是怎麼想出來的啊......代碼:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int n,c;
    struct node{
        int w,p;
    }t[55];
    int f[55][55][2];
    int sum[55];//前綴和 
    //int cal(int i,int j,int l,int r)//i,j路燈編號,l,r區間左右 
    //{
    //    return (t[j].p-t[i].p)*(sum[n]-(sum[r]-sum[l-1]));//除去這個區間 
    //}
    int main()
    {
        scanf("%d%d",&n,&c);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&t[i].p,&t[i].w);
            sum[i]=sum[i-1]+t[i].w;
        }
        //初始化
      memset(f,
    127,sizeof(f)); f[c][c][0]=f[c][c][1]=0; for(int k=2;k<=n;k++)//枚舉區間長度 for(int i=1;i<=n-(k-1);i++){//枚舉左端點 int j=i+k-1; f[i][j][0]=min(f[i+1][j][0]+(t[i+1].p-t[i].p)*(sum[n]-(sum[j]-sum[i])),//從i+1的左端點往左走一個關i路燈 //sum[j]-sum[i]是因為過程中i並沒有被關 f[i+1][j][1]+(t[j].p-t[i].p)*(sum[n]-(sum[j]-sum[i])));//或者從i+1,j的右端點走到左端來關i路燈 f[i][j][1]=min(f[i][j-1][1]+(t[j].p-t[j-1].p)*(sum[n]-(sum[j-1]-sum[i-1])),//這裡j沒被關而且i還要-1 f[i][j-1][0]+(t[j].p-t[i].p)*(sum[n]-(sum[j-1]-sum[i-1])) ); } printf("%d",min(f[1][n][1],f[1][n][0])); }

    還是不太會做區間dp啊

     2019-03-08 19:00:02

    又出現了一個新問題,這樣枚舉所有區間最後得到的最小答案一定是從 c 出發的嗎?

    我認為是這樣的:我們是從小到大枚舉的區間長度和起始位置,每一個小區間都是由更小的區間推出來的,

    而最小的區間也就是1長度的區間是由我們手動賦初值的,也就是區間長度為1的時候在c點最優,

    這樣就保證了每一個由c點推出來的區間都是最優解這樣就保證了最後1~n的區間也是最優的。

  • 相关阅读:
    yolo_to_onnx ValueError: need more tan 1 value to unpack
    yolo_to_onnx killed
    C++ 实现二维矩阵的加减乘等运算
    Leetcode 1013. Partition Array Into Three Parts With Equal Sum
    Leetcode 1014. Best Sightseeing Pair
    Leetcode 121. Best Time to Buy and Sell Stock
    Leetcode 219. Contains Duplicate II
    Leetcode 890. Find and Replace Pattern
    Leetcode 965. Univalued Binary Tree
    Leetcode 700. Search in a Binary Search Tree
  • 原文地址:https://www.cnblogs.com/superminivan/p/10479435.html
Copyright © 2011-2022 走看看