[Java解法]洛谷-P1220-关路灯-区间动态规划

题目描述:

关路灯 - 洛谷https://www.luogu.com.cn/problem/P1220

题目的主要信息提取:

        题目会给我们n对数列,每队数列代表一个路灯的位置和他的功率,这里的位置指的是绝对位置,你可以理解为一个数轴上从0开始的绝对位置,我们结合题目给的测试用例去解释:

        

5 3
2 10
3 20
5 20
6 30
8 10

第一行时代表有5个路灯,维修人老张住在第三个路灯的位置,后面五行数据,第一行代表的是第一个路灯,以此类推

第一个数据其中的2代表的是第一个路灯在的绝对位置是2,他的功率时10W/S

思路分析:

        我们可以将这个数据画出来更加直观

这样题目所给的测试用例就清晰明了了

        当老李在三号灯出门的时候就可以随手关掉3号灯,所以在三号灯被关掉的时候是不花费时间的,所以此时的状态消耗的电力为0,我们此时有两种方案,向左走或者是向右走,无论如何,当老李走完一个区域时,所有的这个区域的灯一定是被关掉的,例如2号灯到5号灯这个区域,老李既可以从3号->4号->5号->2号也可以从3->2->4->5也可以从3->4->2->5,无论老李采用什么方案,他关完一个区域的所有的灯时,他一定在这个区域的左边界或者是右边界处,我们通过填表法,可以看出来当范围向左或向右扩大一个位置时,他一定是可以从他的子区域的左侧或右侧边界度过过来的,如果不理解可以再读一下我前面解释的那句话。例如关闭1号到5号灯的区域的灯后消耗的电,只有这几种情况,关掉2号灯到5号灯然后再去关1号灯,或者是关掉1号灯到4号灯,然后去关5号灯。

我们定义两个数组left[i][j]和right[i][j]

        left[i][j]就是关闭了i-j区域的灯后老李站在i处时消耗的电         

        right[i][j]就是关闭了i-j区域的灯后老李站在j处时消耗的电         

我们通过上面标红的文字描述可以写出基本的状态转移方程:

left[i][j] = min(left[i+1][j]+/从i+1走到i所消耗的电力/,right[i+1][j]+/从j走到i所消耗的电力/)

right[i][j] = min(right[i][j-1]+/从j-1走到j所消耗的电力/,left[i][j-1]+/从i走到j所消耗的电力/)

从i到j所消耗的电力我们使用前缀和来计算

第一个状态转移方程如何计算?

当我们关闭了i+1->j区域的灯后,剩下的灯的总功率乘以从i+1走到i的路程就是这段时间额外消耗的电力,这些电力加上之前的就是总共这个区域再关闭前消耗的最少的电力,由于i+1->j区域已经关闭了所以我们不需要考虑他们的功率,我们如何获得其他的灯的功率的总和呢?

我们要求的图中标红的区域的功率和,从图中可以清楚的看出来我们只需要用绿色的区域减去蓝色的区域的功率然后加上前面的红色区域就是剩余的路灯的功率和。

代码书写:

        

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] tags= reader.readLine().split(" ");
        int n = Integer.parseInt(tags[0]);
        int selfNo = Integer.parseInt(tags[1]);
        int[] power = new int[n+1];
        int[] pos = new int[n+1];
        int[][] left = new int[n+1][n+1];
        int[][] right = new int[n+1][n+1];
        for(int i =1;i<=n;i++){
            String[] temp = reader.readLine().split(" ");
            pos[i] = Integer.parseInt(temp[0]);
            power[i] = power[i-1]+Integer.parseInt(temp[1]);
            Arrays.fill(left[i],Integer
            .MAX_VALUE/2);
            Arrays.fill(right[i],Integer.MAX_VALUE/2);
        }
        right[selfNo][selfNo] = left[selfNo][selfNo] = 0;
        for(int i = selfNo;i>0;i--){
            for(int j = i+1;j<=n;j++){
                int exclude = (power[n]+power[i-1]-power[j-1]);
                right[i][j] = Math.min(right[i][j-1]+exclude*(pos[j]-pos[j-1]),left[i][j-1]+exclude*(pos[j]-pos[i]));
                int exclude2 = (power[n]+power[i]-power[j]);
                left[i][j] = Math.min(left[i+1][j]+exclude2*(pos[i+1]-pos[i]),right[i+1][j]+exclude2*(pos[j]-pos[i]));
            }
        }
        System.out.println(Math.min(left[1][n],right[1][n]));
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值