Java解法,贪心算法与区间合并的一道好题
To Fill or Not to Fill
http://www.nowcoder.com/questionTerminal/f7eba38f7cd24c45982831e0f38518f9
本题解思路来源于其他人的题解,在他的基础上进行了优化。
先提供一份他人的题解,我将其翻译成了java语言版本。
import java.util.*; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int c = scanner.nextInt(); int d = scanner.nextInt(); int avg = scanner.nextInt(); int n = scanner.nextInt(); List<Pair> list = new ArrayList<>(); for (int i = 0; i < n; i++) { double price = scanner.nextDouble(); int distance = scanner.nextInt(); list.add(new Pair(price,distance)); } list.sort(Comparator.comparing(a -> a.price)); boolean[] visited = new boolean[d]; int maxDistance = c * avg; double cost = 0.0; for (int i = 0; i < n; i++) { Pair pair = list.get(i); int need = (pair.distance+maxDistance < d ? maxDistance : d - pair.distance); int cnt = 0; for (int j = pair.distance; j < pair.distance+need; j++) { if (!visited[j]){ visited[j] = true; cnt++; } } cost += cnt/(avg*1.0)*pair.price; } int i; for (i = 0; i < d; i++) { if (!visited[i]){ System.out.printf("The maximum travel distance = %.2f\n",(double)i); break; } } if (i == d) System.out.printf("%.2f\n",cost); } } class Pair{ Double price; int distance; public Pair(Double price, int distance) { this.price = price; this.distance = distance; } }
上面的题解逻辑清晰,使用visited数组也非常巧妙,基本上不需要注释也能理解。但是存在的问题是visited数组本身空间代价比较高,当距离较大时,无论是时间复杂度和空间复杂度都会有很大的开销。
实际上visited数组有用的部分主要是两个端点距离,即begin和end,中间的数据意义并不大。在此思路上进行优化,只存储两个区间端点,维护一个不相交的区间,产生新的区间后将区间进行合并。
import java.util.*; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int c = scanner.nextInt(); int d = scanner.nextInt(); int avg = scanner.nextInt(); int n = scanner.nextInt(); List<Pair> list = new ArrayList<>(); for (int i = 0; i < n; i++) { double price = scanner.nextDouble(); int distance = scanner.nextInt(); list.add(new Pair(price,distance)); } list.sort(Comparator.comparing(a -> a.price)); int maxDistance = c * avg; double cost = 0.0; Queue<Interval> queue = new PriorityQueue<>(Comparator.comparingInt(a->a.start)); for (int i = 0; i < n; i++) { Pair pair = list.get(i); int need = (pair.distance+maxDistance < d ? maxDistance : d - pair.distance); int cnt = 0; Interval interval = new Interval(pair.distance,pair.distance+need); int len = 0; Iterator<Interval> iterator = queue.iterator(); while (iterator.hasNext()){ Interval cur = iterator.next(); len += cur.end - cur.start; if (cur.start <= interval.end && cur.end >= interval.start){ //区间相交 iterator.remove(); int min = Math.min(cur.start,interval.start); int max = Math.max(cur.end,interval.end); interval = new Interval(min,max); //区间合并 } } queue.offer(interval); int afterLen = 0; for (Interval next : queue) { afterLen += next.end - next.start; } cnt += afterLen - len; //前后区间差值即为加油量 cost += cnt/(avg*1.0)*pair.price; } Interval interval = queue.poll(); //最后能行进的区间范围 assert interval != null; if (interval.start == 0 && interval.end == d){ System.out.printf("%.2f\n",cost); }else { System.out.printf("The maximum travel distance = %.2f\n",(double)interval.end); } } } class Pair{ Double price; int distance; public Pair(Double price, int distance) { this.price = price; this.distance = distance; } } class Interval{ int start; int end; public Interval(int start, int end) { this.start = start; this.end = end; } }