有N堆蘋果,每堆蘋果有x,y,w三個屬性,每次可以進行的操做是:把一堆蘋果移動到另外一堆蘋果中,移動蘋果所須要花費的力氣爲$w \times (|x_1-x_2|+|y_1-y_2|$ ,問最少須要花費多少力氣才能把這些蘋果移動到一堆去?java
暴力解法複雜度爲$O(n^2)$,問題等價於尋找一箇中心點。
這個問題關鍵在於距離的定義比較特殊。若是距離定義爲歐式距離,這個問題就變得很是艱難(好比費馬點),這是一個NP問題。
由於距離定義比較特殊,因此x方向和y方向是徹底無關的。能夠分開考慮這二者。首先把這個問題看作一維來考慮。數組
引題
一條路上分佈着N個村莊,每一個村莊都有一個座標,如今要在這條路上修建一個水站,使得這N個村莊到水站的距離之和最短。
這個問題的結論是:把水站創建在中位數上。若是村莊個數爲奇數個,那麼正好有一箇中位數;若是村莊個數爲偶數個,那麼水站創建在中間兩個村莊之間。this
回到本題中來,由於x和y是無關的,因此能夠先求出最優的x和最優的y。這樣一來,把所有蘋果移動到x,y處是最恰當的,可是隻能把蘋果移動到有蘋果的點,因此只能找x,y附近的一些點,這是由於整個二維平面是一個凸面,必然只有一個最小點。只須要枚舉離中間點最近的幾個點便可,這麼作固然是不嚴謹的。很容易舉出反例來。code
import java.util.Arrays; import java.util.Comparator; import java.util.Scanner; public class Main { final int MAXN = (int) (1e5 + 7); class Point { long x, y, w; Point(int x, int y, int w) { this.x = x; this.y = y; this.w = w; } } Point find(Point[] a, long total) { int cnt = 0; long half = total >> 1; for (Point i : a) { cnt += i.w; if (cnt >= half) { return i; } } return a[a.length - 1]; } Main() { Scanner cin = new Scanner(System.in); int N = cin.nextInt(); Point[] a = new Point[N]; long total = 0; for (int i = 0; i < N; i++) { a[i] = new Point(cin.nextInt(), cin.nextInt(), cin.nextInt()); total += a[i].w; } Arrays.sort(a, Comparator.comparingInt(m -> (int) (m.x))); long x = find(a, total).x; Arrays.sort(a, Comparator.comparingInt(m -> (int) (m.y))); long y = find(a, total).y; Arrays.sort(a, Comparator.comparingInt(m -> (int) (Math.abs(m.x - x) + Math.abs(m.y - y)))); long ans = Long.MAX_VALUE; for (int j = 0; j < Math.min(50, N); j++) { long s = 0; for (Point i : a) { s += i.w * (Math.abs(i.x - a[j].x) + Math.abs(i.y - a[j].y)); } ans = Math.min(ans, s); } System.out.println(ans); } public static void main(String[] args) { new Main(); } }
N個狙擊手,每一個狙擊手瞄準一我的(這我的有多是他本身!)。全部狙擊手不會同時開槍。你能夠隨意安排全部狙擊手開槍的次序,直到沒法再開槍爲止,問:通過一番廝殺以後,最多幸存幾個狙擊手、最少倖存幾個狙擊手?隊列
這個問題是兩問:一問是狙擊手最多活幾個,一問是狙擊手最少活幾個。
一眼看上去,狙擊手之間構成的結構是圖。而實際上,這個圖很是特殊:每一個狙擊手只能瞄準一我的,因此每一個結點的出度必然是1,而入度能夠不少。ci
首先考慮最多幸存幾個狙擊手。很顯然,那些未被瞄準的人必然會倖存下來。可是這些人是必定要開槍的。這些「必然不死者」亂槍事後,會拯救一批新的「必然不死者」,「必然不死者」開槍以後又會拯救不死者,這樣一直到所有「不死者」都沒法開槍爲止。因此這個問題能夠用優先隊列來解決:優先讓必然不死者開槍(他們是必定要開槍的)。it
接下來考慮最少倖存幾個狙擊手。這就須要深入理解這個問題的特殊之處:每一個結點出度爲1。須要看清楚「單出度圖」的一個特色:若是含有環,只能是形如「0」和形如「6」這樣的環,不可能出現形如「8」的環。而每一個形如6的環最少倖存一我的。因而,問題轉化爲:整個圖中有多少個「6」。io
import java.util.*; public class Main { class Node { int id, cnt; Node(int id, int cnt) { this.id = id; this.cnt = cnt; } } int nonzero(boolean died[]) { int s = 0; for (int i = 1; i < died.length; i++) { if (!died[i]) s++; } return s; } Main() { Scanner cin = new Scanner(System.in); int N = cin.nextInt(); int a[] = new int[N + 1]; int b[] = new int[N + 1];//bi表示想殺i的人的個數 for (int i = 1; i <= N; i++) { a[i] = cin.nextInt(); } for (int i = 1; i <= N; i++) b[a[i]]++; PriorityQueue<Node> q = new PriorityQueue<>(Comparator.comparing(x -> x.cnt)); for (int i = 1; i <= N; i++) { q.add(new Node(i, b[i])); } boolean died[] = new boolean[N + 1]; for (int i = 1; i <= N; i++) if (a[i] == i) died[i] = true; while (!q.isEmpty()) { int now = q.poll().id; if (died[now]) continue; if (!died[a[now]]) { died[a[now]] = true; int next = a[a[now]];//個人下下家獲得解放 b[next]--; q.add(new Node(next, b[next])); } } int maxLive = nonzero(died); Arrays.fill(died, false); for (int i = 1; i <= N; i++) if (a[i] == i) died[i] = true; int ring[] = new int[N + 1];//ring i是否在環上 for (int i = 1; i <= N; i++) ring[i] = i; for (int i = 1; i <= N; i++) { if (died[i]) continue; int now = i; while (!died[a[now]] && a[now] != i) { died[a[now]] = true; now = a[now]; } if (a[now] == i) {//若是成環了,要讓環的祖先承擔後續損失 int j = i; while (true) { ring[j] = i; j = a[j]; if (j == i) break; } } if (ring[a[now]] != a[now]) { died[ring[a[now]]] = true; } } int minLive = nonzero(died); System.out.println(minLive); System.out.println(maxLive); } public static void main(String[] args) { new Main(); } }
給定K個長度爲N的數組,要求一個最小區間(區間長度儘可能小),要求這個最小區間包含的數字跟K個數組中任一數組的交集都不爲空。class
優先隊列+單調隊列很容易處理。import
import java.util.*; public class Main { class Point { int x, k, index; Point(int x, int k, int index) { this.x = x; this.k = k; this.index = index; } } Main() { Scanner cin = new Scanner(System.in); int K = cin.nextInt(); int N = cin.nextInt(); PriorityQueue<Point> q = new PriorityQueue<>(Comparator.comparing(x -> x.x)); for (int i = 0; i < K; i++) { for (int j = 0; j < N; j++) { int x = cin.nextInt(); q.add(new Point(x, i, j)); } } int[] inqCount = new int[K]; Queue<Point> qq = new LinkedList<>();//單調隊列 int nonzeroCount = 0; int ans = Integer.MAX_VALUE; int beg = 0, end = 0; boolean startCheck = false;//是否開始覆蓋 while (!q.isEmpty()) { Point now = q.poll(); qq.add(now); inqCount[now.k]++; if (inqCount[now.k] == 1) { nonzeroCount++; if (nonzeroCount == K) { startCheck = true; } } //彈出無用元素 while (!qq.isEmpty() && inqCount[qq.peek().k] > 1) { inqCount[qq.peek().k]--; qq.poll(); } if (startCheck) { int minValue = qq.peek().x; int nowAns = now.x - minValue; if (nowAns < ans) { ans = nowAns; beg = minValue; end = now.x; } } } System.out.println(beg + " " + end); } public static void main(String[] args) { new Main(); } }