題目:
題目的大致意思是,給定n個閉區間,並且這個閉區間上的點都是整數,現在要求你使用最少的點來覆蓋這些區間並且每個區間的覆蓋的點的數量滿足輸入的要求點覆蓋區間的數量。
輸入:
第一行輸入n,代表n個區間。
接下來的n行每行的第一個數代表區間起點,第二個數代表區間終點,第三個數代表這個區間必須要選取的點的數量。
輸出:
輸出最少的點的數量,這些最少的點要覆蓋全部區間。
這個題是區間選點問題的一種變體,但是我們對於區間選點問題清楚之后那么這種題目也是一樣解決的,只不過需要在某些地方特別處理一下。這道題目跟區間調度的問題非常類似,我們也可以采用區間調度問題的策略運用到這道題目上面,盡量往結束時間的端點來選點因為這樣做我們可以使盡量少的點覆蓋更多的區間,之后就是選點的問題了,當我們選擇了這個點之后需要標記一下,定義一個數軸來記錄其中標記過的點,以防下一次在選點的時候重復選擇。而且在for循環中依次掃描這些給定的區間,看這個區間需要覆蓋的點的數量是多少(有可能這個區間的標記的點出現在另外的區間上那么這個時候我們就選擇跳過這個點)
代碼:
1 import java.util.Arrays; 2 import java.util.Scanner; 3 4 public class 區間選點問題1 { 5 public static void main(String[] args) { 6 Scanner sc = new Scanner(System.in); 7 int n = sc.nextInt(); 8 Interval[] intervals = new Interval[n]; 9 for (int i = 0; i < n; i++) { 10 intervals[i] = new Interval(sc.nextInt(), sc.nextInt(), sc.nextInt()); 11 } 12 Arrays.sort(intervals);// 按區間右端點排序 13 14 int max = intervals[n - 1].t;// 右端最大值 15 int[] axis = new int[max + 1];// 標記數軸上的點是否已經被選中 16 // int[] sums = new int[max + 1]; 17 for (int i = 0; i < n; i++) { 18 // 1.查閱區間中有多少個點 19 int s = intervals[i].s;// 起點 20 int t = intervals[i].t;// 終點 21 int cnt = sum(axis, s, t);// 找到這個區間已經選點的數量, 22 //sums[t] - sums[s - 1]; 效率低 23 // 2.如果不夠,從區間右端開始標記,遇標記過的就跳過 24 intervals[i].c -= cnt;// 需要新增的點的數量 25 while (intervals[i].c > 0) { 26 if (axis[t] == 0) {// 從區間終點開始選點 27 axis[t] = 1; 28 // updateSums(t,sums);//更新前綴和 29 intervals[i].c--;// 進一步減少需要新增的點的數量 30 t--; 31 } else {// 這個點已經被選過了 32 t--; 33 } 34 } 35 36 } 37 System.out.println(sum(axis, 0, max)); 38 } 39 40 /** 41 * 統計數軸axis上s-t區間已經有多少個點被選中 42 * 43 * @param axis 44 * @param s 45 * @param t 46 * @return 47 */ 48 private static int sum(int[] axis, int s, int t) { 49 int sum = 0; 50 for (int i = s; i <= t; i++) { 51 sum += axis[i]; 52 } 53 return sum; 54 } 55 56 private static void updateSums(int t, int[] sums) { 57 for (int i = t; i < sums.length; i++) { 58 sums[i]++; 59 } 60 } 61 62 private static class Interval implements Comparable<Interval> { 63 int s; 64 int t; 65 int c; 66 67 public Interval(int s, int t, int c) { 68 this.s = s; 69 this.t = t; 70 this.c = c; 71 } 72 73 @Override 74 public int compareTo(Interval other) { 75 int x = this.t - other.t; 76 if (x == 0) 77 return this.s - other.s; 78 else 79 return x; 80 } 81 } 82 }
結果:
但是上面這個代碼提交到OJ上面會發生超時,代碼邏輯本身沒有什么問題,關鍵是在掃描每個區間的時候消耗的時間比較多導致了超時,但是我們可以使用另外的一種數據結構來解決,那就是樹狀數組,降低掃描區間的時間復雜度。關於樹狀數組現在不太懂,而且也脫離了貪心算法的范疇,現在把代碼記錄下來,留作以后再看。
代碼:

1 import java.util.Arrays; 2 import java.util.Scanner; 3 4 public class 區間選點問題2 { 5 public static void main(String[] args) { 6 Scanner sc = new Scanner(System.in); 7 int n = sc.nextInt(); 8 Interval[] intervals = new Interval[n]; 9 for (int i = 0; i < n; i++) { 10 intervals[i] = new Interval(sc.nextInt(), sc.nextInt(), sc.nextInt()); 11 } 12 Arrays.sort(intervals);// 按區間右端點排序 13 14 int max = intervals[n - 1].t;// 右端最大值 15 int[] axis = new int[max + 1]; 16 int[] c = new int[max + 2]; 17 // int[] sums = new int[max + 1]; 18 for (int i = 0; i < n; i++) { 19 // 1.查閱區間中有多少個點 20 int s = intervals[i].s;// 起點 21 int t = intervals[i].t;// 終點 22 int cnt = sum(t + 1, c, max + 1) - sum(s, c, max + 1);// sum(axis,s,t);//sums[t] 23 // - sums[s 24 // - 25 // 1];//效率低 26 // 2.如果不夠,從區間右端開始標記,遇標記過的就跳過 27 intervals[i].c -= cnt; 28 while (intervals[i].c > 0) { 29 if (axis[t] == 0) { 30 axis[t] = 1; 31 update(t + 1, 1, c, max + 1); 32 intervals[i].c--; 33 t--; 34 } else { 35 t--; 36 } 37 } 38 39 } 40 System.out.println(sum(max + 2, c, max + 1)); 41 } 42 43 /** 44 * 更新樹狀數組c,注意i是項數,不是下標,而是下標+1 45 */ 46 private static void update(int i, int delta, int[] c, int n) { 47 for (; i <= n; i += lowbit(i)) { 48 c[i] += delta; 49 } 50 } 51 52 /** 53 * 前i項和,注意:i不是下標 54 * 55 * @param i 56 * @return 57 */ 58 private static int sum(int i, int[] c, int n) { 59 int sum = 0; 60 if (i > n) 61 i = n; 62 for (; i > 0; i -= lowbit(i)) { 63 sum += c[i]; 64 } 65 return sum; 66 } 67 68 /** 69 * 它通過公式來得出k,其中k就是該值從末尾開始1的位置。 然后將其得出的結果加上x自身就可以得出當前節點的父親節點的位置 70 * 或者是x減去其結果就可以得出上一個父親節點的位置。 71 * 比如當前是6,二進制就是0110,k為2,那么6+2=8,C(8)則是C(6)的父親節點的位置; 72 * 相反,6-2=4,則是C(6)的上一個父親節點的位置。 73 */ 74 static int lowbit(int x) { 75 return x - (x & (x - 1)); 76 } 77 78 private static class Interval implements Comparable<Interval> { 79 int s; 80 int t; 81 int c; 82 83 public Interval(int s, int t, int c) { 84 this.s = s; 85 this.t = t; 86 this.c = c; 87 } 88 89 @Override 90 public int compareTo(Interval other) { 91 int x = this.t - other.t; 92 if (x == 0) 93 return this.s - other.s; 94 else 95 return x; 96 } 97 } 98 }