有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(); } }