zoukankan      html  css  js  c++  java
  • [Dijkstra贪心思想妙用]真实笔试题:传送门

    * @description: 西西所在的国家有N座城市,每座城市有一道传送门,城市i的传送门通往城市a[i]
    *               当西西位于城市i时,每次他可以执行以下三种操作中的一种:
    *                   花费A的费用,从城市i前往城市a[i];
    *                   如果a[i]>1,可以花费B的费用,将a[i]的值减少1;
    *                   如果a[i]<N,可以花费C的费用,将a[i]的值增加1。
    *               现在,西西想从城市1前往城市N,那么他至少花费多少费用?
    *
    *               输入:
    *               第一行输入四个整数 N、A、B、C(1<=N<=10000,1<=A,B,C<=100000)
    *               第二行输入N个整数,a[1]到a[N](1<=a[i]<=N)
    *
    *               输出:
    *               输出一个整数,表示从城市1前往城市N所花费的最少费用
    *
    *               样例输入
    *               7 1 1 1
    *               3 6 4 3 4 5 6
    *
    *               样例输出
    *               4
    *
    *               样例解释:
    *               将a[1]减少1,此时a[1]=2*               从城市1前往城市2;
    *               将a[2]增加1,此时a[2]=7;
    *               从城市2前往城市7

    这题拿到手第一反应是dp,但在列了一下例子之后,发现了其中的规律,做着做着发现跟Dijkstra的贪心思想竟然几乎完全吻合。

    首先定义一个shortest[]数组,shortest[i]表示城市i到城市N的最小花费。并维护一个OPEN集合和CLOSE集合,记录还未确定最小花费的城市和已经确定的城市。

    以样例为例,首先shortest[N] = 0,将N加入CLOSE,其他全在OPEN集合中,并计算每一个城市通过自增或自减操作+移动,到达N的花费

    7 1 1 1
    3 6 4 3 4 5 6

    shortest = [4C+A,C+A,3C+A,4C+A,3C+A,2C+A,0]

    从中选择一个或多个在OPEN集合中,并且shortest值最小的城市,此时这个值,就是最终的shortest的值。

    这是一个很关键的结论,Dijkstra的关键思想也是在每一轮求出一个节点的最短路径时,就把求得的最短路径当作最终的最短路径。

    在这题中,可以通过反证来想一想,记当前shortest值最小的城市为城市i,如果这个节点当前的值不是最终的值,那么城市i一定要先移动到其他城市,再移动到城市N,那么由于其他城市的shortest的值均大于他,且移动一次的花费是A。假设城市i通过移动到城市j再移动到城市N,那么花费为A+shortest[j],由于shortest[j] >= shortest[i],且A>0,那么A+shortest[j]一定大于shortest[i],也就是说当前的shortest[i]一定是最优值。

    那么理解了这一步,一切就很容易了,将当前shortest值最小的城市,加入到CLOSE表里,当作下一轮的中间节点。

    我在这里稍微优化了一下,每一次更新CLOSE表的时候,将CLOSE表先清空,原因是因为CLOSE表中原来的城市已经考虑过了,后续不需要再计算。

    再下一轮迭代的时候,就将所有CLOSE表中的城市当作中间城市,再更新一遍shortest数组就可以了。

    当CLOSE表中出现城市1的时候,结束循环,因为我们只需要知道shortest[1]就行了。

        public static void main(String[] args){
            Scanner sc = new Scanner(System.in);
            int N;
            long A,B,C;
            N = sc.nextInt();
            A = sc.nextLong();
            B = sc.nextLong();
            C = sc.nextLong();
            int[] a = new int[N];
    //al相当于CLOSE表,no相当于OPEN表 HashSet
    <Integer> al = new HashSet<>(); HashSet<Integer> no = new HashSet<>(); for(int i = 0; i < N; i++){ a[i] = sc.nextInt(); no.add(i+1); } long[] shortest = new long[N]; for(int i = 0; i < N; i++){ shortest[i] = A + C * (N - a[i]); } no.remove(N); al.add(N); shortest[N-1] = 0; while(!al.contains(1)){ long min = Long.MAX_VALUE; for(Integer tar : al){ for(Integer st : no){ if(a[st-1] == tar){ shortest[st-1] = Math.min(shortest[tar-1] + A,shortest[st-1]); } else if(a[st-1] > tar){ shortest[st-1] = Math.min(shortest[tar-1] + (a[st-1] - tar) * B + A,shortest[st-1]); } else{ shortest[st-1] = Math.min(shortest[tar-1] + (tar - a[st-1]) * C + A,shortest[st-1]); } min = Math.min(min,shortest[st-1]); } } al.clear(); Iterator<Integer> iterator = no.iterator(); while (iterator.hasNext()){ Integer in = iterator.next(); if (shortest[in-1] == min){ al.add(in); iterator.remove(); } } } System.out.println(shortest[0]); }

     表述的可能不是很清楚,如有疑问可以评论指出。

  • 相关阅读:
    memcached与.NET的融合使用(二)
    memcached与.NET的融合使用(一)
    使用window2003安装邮件服务器最新实际操作记录
    2014 -> 2015
    数据挖掘入门 资料和步骤
    CSDN 论坛招聘区是不是有潜规则?在Cnblog招个人试试...
    C#薪水和前途
    面试准备
    面试准备
    面试准备
  • 原文地址:https://www.cnblogs.com/liusandao/p/12739542.html
Copyright © 2011-2022 走看看