Recently, more and more illegal street races have been spotted at night in the city, and they have become a serious threat to public safety. Therefore, the Police Chief has decided to deploy speed cameras on the streets to collect evidence.
There are N+1 intersections in the city, connected by N roads. Every road has the same length of 1. A street race may take place between any two different intersections by using the roads connecting them. Limited by their budget, the police are able to deploy at most K speed cameras on these N roads. These K speed cameras should be installed such that the length of any possible street race route not covered by speed cameras should be as short as possible.
You are given a map of the city in the form of two arrays, A and B of length N, and an integer K:
- For each J (0 ≤ J < N) there is a road connecting intersections A[J] and B[J].
The Police Chief would like to know the minimum length of the longest path out of surveillance after placing at most K speed cameras.
Write a function:
class Solution { public int solution(int[] A, int[] B, int K); }
that, given arrays A and B of N integers and integer K, returns the minimum length of the longest path unmonitored by speed cameras after placing at most K speed cameras.
For example, given K = 2 and the following arrays:
A[0] = 5 B[0] = 1 A[1] = 1 B[1] = 0 A[2] = 0 B[2] = 7 A[3] = 2 B[3] = 4 A[4] = 7 B[4] = 2 A[5] = 0 B[5] = 6 A[6] = 6 B[6] = 8 A[7] = 6 B[7] = 3 A[8] = 1 B[8] = 9the function should return 2. Two speed cameras can be installed on the roads between intersections 1 and 0 and between intersections 0 and 7. (Another solution would be to install speed cameras between intersections 0 and 7 and between intersections 0 and 6.) By installing speed cameras according the first plan, one of the longest paths without a speed camera starts at intersection 8, passes through intersection 6 and ends at intersection 3, which consists of two roads. (Other longest paths are composed of intersections 5, 1, 9 and 7, 2, 4).
Write an efficient algorithm for the following assumptions:
- N is an integer within the range [1..50,000];
- each element of arrays A and B is an integer within the range [0..N];
- K is an integer within the range [0..N];
- the distance between any two intersections is not greater than 900.
// you can also use imports, for example:
// import java.util.*;
// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");
import java.util.*;
public class Solution {
private int rootIndex = 0;
private int largestId = 0;
private int sortedSubtreeDiameters[] = new int[50001];
private Node nodeMap[] = new Node[50001];
private boolean visited[] = new boolean[50001];
private int diameter[] = new int[50001];
static class Node {
public int id;
public List<Integer> edgeList;
public Node(int v) {
this.id = v;
edgeList = new ArrayList<Integer>();
}
public void addEdge(Integer t) {
edgeList.add(t);
}
}
public int solution(int[] A, int[] B, int K) {
int n = A.length;
for (int i = 0; i < n; i++) {
if (nodeMap[A[i]] == null) {
nodeMap[A[i]] = new Node(A[i]);
}
if (nodeMap[B[i]] == null) {
nodeMap[B[i]] = new Node(B[i]);
}
final Node na = nodeMap[A[i]];
final Node nb = nodeMap[B[i]];
na.addEdge(B[i]);
nb.addEdge(A[i]);
largestId = Math.max(largestId, A[i]);
largestId = Math.max(largestId, B[i]);
}
rootIndex = A[0];
int res = Integer.MAX_VALUE;
int low = 0, high = Math.min(900, largestId);
while (low <= high) {
int mid = (low + high) / 2;
if (isAvailabel(K, mid)) {
res = Math.min(res, mid);
high = mid - 1;
} else {
low = mid + 1;
}
}
return res;
}
public boolean isAvailabel(int cameraLimit, int notCoveredLength) {
for (int i = 0; i < visited.length; i++) visited[i] = false;
for (int i = 0; i < diameter.length; i++) diameter[i] = -1;
if (dfs(nodeMap[rootIndex], notCoveredLength) > cameraLimit) {
return false;
}
return true;
}
private int dfs(Node node, int limit) {
if (node == null || visited[node.id]) {
return 0;
}
visited[node.id] = true;
int counter = 0;
for (Integer id : node.edgeList) {
counter += dfs(nodeMap[id], limit);
}
/*
@
/ \
@ @(current)
/ \ / \\\
@ @ a b c d
step 1: Sort diameters of subtrees in reverse order, store them in an array
step 2: For each adjacent pair in the sorted array, e.g. [a, b], [b, c], [c, d].
If a + b + 2 > limit, we break the edge connecting current node to a, and counter plus 1.
Do the same for [b, c] and [c, d]
step 3: For last child d. or if current node has only one child.
If d + 1 > limit, we break the edge connecting current node to a, and counter plus 1.
step 4: Set diameter of current node to the largest one of the remains(not broken ones).
*/
/* step 1 */
int n = 0;
for (Integer id : node.edgeList) {
if (diameter[id] != -1) {
sortedSubtreeDiameters[n++] = (diameter[id]);
}
}
Arrays.sort(sortedSubtreeDiameters, 0, n);
for (int i = 0; i < n / 2; i++) { // reverse order
int temp = sortedSubtreeDiameters[i];
sortedSubtreeDiameters[i] = sortedSubtreeDiameters[n - 1 - i];
sortedSubtreeDiameters[n - 1 - i] = temp;
}
/* step 2 */
int maxDiameterAfterRemoval = -1;
for (int i = 0; i < n - 1; i++) {
if (sortedSubtreeDiameters[i] + sortedSubtreeDiameters[i+1] + 2 > limit) {
counter++;
} else {
maxDiameterAfterRemoval = Math.max(maxDiameterAfterRemoval, sortedSubtreeDiameters[i]);
}
}
/* step 3 */
if (n >= 1) {
int i = n - 1;
if (sortedSubtreeDiameters[i] + 1 > limit) {
counter++;
} else {
maxDiameterAfterRemoval = Math.max(maxDiameterAfterRemoval, sortedSubtreeDiameters[i]);
}
}
/* step 4 */
// if subtrees are all removed, we can treat current node as a leaf, so diameter becomes 0
if (maxDiameterAfterRemoval == -1) {
diameter[node.id] = 0;
} else { // if still some subtrees remain, then set current node's diameter as one plus the largest
diameter[node.id] = maxDiameterAfterRemoval + 1;
}
return counter;
}
}
// you can also use imports, for example:
// import java.util.*;
// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");
import java.util.*;
public class Solution {
private int rootIndex = 0;
private int largestId = 0;
private int sortedSubtreeDiameters[] = new int[50001];
private Node nodeMap[] = new Node[50001];
private boolean visited[] = new boolean[50001];
private int diameter[] = new int[50001];
static class Node {
public int id;
public List<Integer> edgeList;
public Node(int v) {
this.id = v;
edgeList = new ArrayList<Integer>();
}
public void addEdge(Integer t) {
edgeList.add(t);
}
}
public int solution(int[] A, int[] B, int K) {
int n = A.length;
for (int i = 0; i < n; i++) {
if (nodeMap[A[i]] == null) {
nodeMap[A[i]] = new Node(A[i]);
}
if (nodeMap[B[i]] == null) {
nodeMap[B[i]] = new Node(B[i]);
}
final Node na = nodeMap[A[i]];
final Node nb = nodeMap[B[i]];
na.addEdge(B[i]);
nb.addEdge(A[i]);
largestId = Math.max(largestId, A[i]);
largestId = Math.max(largestId, B[i]);
}
rootIndex = A[0];
int res = Integer.MAX_VALUE;
int low = 0, high = Math.min(900, largestId);
while (low <= high) {
int mid = (low + high) / 2;
if (isAvailabel(K, mid)) {
res = Math.min(res, mid);
high = mid - 1;
} else {
low = mid + 1;
}
}
return res;
}
public boolean isAvailabel(int cameraLimit, int notCoveredLength) {
for (int i = 0; i < visited.length; i++) visited[i] = false;
for (int i = 0; i < diameter.length; i++) diameter[i] = -1;
if (dfs(nodeMap[rootIndex], notCoveredLength) > cameraLimit) {
return false;
}
return true;
}
private int dfs(Node node, int limit) {
if (node == null || visited[node.id]) {
return 0;
}
visited[node.id] = true;
int counter = 0;
for (Integer id : node.edgeList) {
counter += dfs(nodeMap[id], limit);
}
/*
@
/ \
@ @(current)
/ \ / \\\
@ @ a b c d
step 1: Sort diameters of subtrees in reverse order, store them in an array
step 2: For each adjacent pair in the sorted array, e.g. [a, b], [b, c], [c, d].
If a + b + 2 > limit, we break the edge connecting current node to a, and counter plus 1.
Do the same for [b, c] and [c, d]
step 3: For last child d. or if current node has only one child.
If d + 1 > limit, we break the edge connecting current node to a, and counter plus 1.
step 4: Set diameter of current node to the largest one of the remains(not broken ones).
*/
/* step 1 */
int n = 0;
for (Integer id : node.edgeList) {
if (diameter[id] != -1) {
sortedSubtreeDiameters[n++] = (diameter[id]);
}
}
Arrays.sort(sortedSubtreeDiameters, 0, n);
for (int i = 0; i < n / 2; i++) { // reverse order
int temp = sortedSubtreeDiameters[i];
sortedSubtreeDiameters[i] = sortedSubtreeDiameters[n - 1 - i];
sortedSubtreeDiameters[n - 1 - i] = temp;
}
/* step 2 */
int maxDiameterAfterRemoval = -1;
for (int i = 0; i < n - 1; i++) {
if (sortedSubtreeDiameters[i] + sortedSubtreeDiameters[i+1] + 2 > limit) {
counter++;
} else {
maxDiameterAfterRemoval = Math.max(maxDiameterAfterRemoval, sortedSubtreeDiameters[i]);
}
}
/* step 3 */
if (n >= 1) {
int i = n - 1;
if (sortedSubtreeDiameters[i] + 1 > limit) {
counter++;
} else {
maxDiameterAfterRemoval = Math.max(maxDiameterAfterRemoval, sortedSubtreeDiameters[i]);
break;
}
}
/* step 4 */
// if subtrees are all removed, we can treat current node as a leaf, so diameter becomes 0
if (maxDiameterAfterRemoval == -1) {
diameter[node.id] = 0;
} else { // if still some subtrees remain, then set current node's diameter as one plus the largest
diameter[node.id] = maxDiameterAfterRemoval + 1;
}
return counter;
}
}
Solution.java:133: error: break outside switch or loop break; ^ 1 error
// you can also use imports, for example:
// import java.util.*;
// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");
import java.util.*;
public class Solution {
private int rootIndex = 0;
private int largestId = 0;
private int sortedSubtreeDiameters[] = new int[50001];
private Node nodeMap[] = new Node[50001];
private boolean visited[] = new boolean[50001];
private int diameter[] = new int[50001];
static class Node {
public int id;
public List<Integer> edgeList;
public Node(int v) {
this.id = v;
edgeList = new ArrayList<Integer>();
}
public void addEdge(Integer t) {
edgeList.add(t);
}
}
public int solution(int[] A, int[] B, int K) {
int n = A.length;
for (int i = 0; i < n; i++) {
if (nodeMap[A[i]] == null) {
nodeMap[A[i]] = new Node(A[i]);
}
if (nodeMap[B[i]] == null) {
nodeMap[B[i]] = new Node(B[i]);
}
final Node na = nodeMap[A[i]];
final Node nb = nodeMap[B[i]];
na.addEdge(B[i]);
nb.addEdge(A[i]);
largestId = Math.max(largestId, A[i]);
largestId = Math.max(largestId, B[i]);
}
rootIndex = A[0];
int res = Integer.MAX_VALUE;
int low = 0, high = Math.min(900, largestId);
while (low <= high) {
int mid = (low + high) / 2;
if (isAvailabel(K, mid)) {
res = Math.min(res, mid);
high = mid - 1;
} else {
low = mid + 1;
}
}
return res;
}
public boolean isAvailabel(int cameraLimit, int notCoveredLength) {
for (int i = 0; i < visited.length; i++) visited[i] = false;
for (int i = 0; i < diameter.length; i++) diameter[i] = -1;
if (dfs(nodeMap[rootIndex], notCoveredLength) > cameraLimit) {
return false;
}
return true;
}
private int dfs(Node node, int limit) {
if (node == null || visited[node.id]) {
return 0;
}
visited[node.id] = true;
int counter = 0;
for (Integer id : node.edgeList) {
counter += dfs(nodeMap[id], limit);
}
/*
@
/ \
@ @(current)
/ \ / \\\
@ @ a b c d
step 1: Sort diameters of subtrees in reverse order, store them in an array
step 2: For each adjacent pair in the sorted array, e.g. [a, b], [b, c], [c, d].
If a + b + 2 > limit, we break the edge connecting current node to a, and counter plus 1.
Do the same for [b, c] and [c, d]
step 3: For last child d. or if current node has only one child.
If d + 1 > limit, we break the edge connecting current node to a, and counter plus 1.
step 4: Set diameter of current node to the largest one of the remains(not broken ones).
*/
/* step 1 */
int n = 0;
for (Integer id : node.edgeList) {
if (diameter[id] != -1) {
sortedSubtreeDiameters[n++] = (diameter[id]);
}
}
Arrays.sort(sortedSubtreeDiameters, 0, n);
for (int i = 0; i < n / 2; i++) { // reverse order
int temp = sortedSubtreeDiameters[i];
sortedSubtreeDiameters[i] = sortedSubtreeDiameters[n - 1 - i];
sortedSubtreeDiameters[n - 1 - i] = temp;
}
/* step 2 */
int maxDiameterAfterRemoval = -1;
for (int i = 0; i < n - 1; i++) {
if (sortedSubtreeDiameters[i] + sortedSubtreeDiameters[i+1] + 2 > limit) {
counter++;
} else {
maxDiameterAfterRemoval = Math.max(maxDiameterAfterRemoval, sortedSubtreeDiameters[i]);
break;
}
}
/* step 3 */
if (n >= 1) {
int i = n - 1;
if (sortedSubtreeDiameters[i] + 1 > limit) {
counter++;
} else {
maxDiameterAfterRemoval = Math.max(maxDiameterAfterRemoval, sortedSubtreeDiameters[i]);
}
}
/* step 4 */
// if subtrees are all removed, we can treat current node as a leaf, so diameter becomes 0
if (maxDiameterAfterRemoval == -1) {
diameter[node.id] = 0;
} else { // if still some subtrees remain, then set current node's diameter as one plus the largest
diameter[node.id] = maxDiameterAfterRemoval + 1;
}
return counter;
}
}
// you can also use imports, for example:
// import java.util.*;
// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");
import java.util.*;
public class Solution {
private int rootIndex = 0;
private int largestId = 0;
private int sortedSubtreeDiameters[] = new int[50001];
private Node nodeMap[] = new Node[50001];
private boolean visited[] = new boolean[50001];
private int diameter[] = new int[50001];
static class Node {
public int id;
public List<Integer> edgeList;
public Node(int v) {
this.id = v;
edgeList = new ArrayList<Integer>();
}
public void addEdge(Integer t) {
edgeList.add(t);
}
}
public int solution(int[] A, int[] B, int K) {
int n = A.length;
for (int i = 0; i < n; i++) {
if (nodeMap[A[i]] == null) {
nodeMap[A[i]] = new Node(A[i]);
}
if (nodeMap[B[i]] == null) {
nodeMap[B[i]] = new Node(B[i]);
}
final Node na = nodeMap[A[i]];
final Node nb = nodeMap[B[i]];
na.addEdge(B[i]);
nb.addEdge(A[i]);
largestId = Math.max(largestId, A[i]);
largestId = Math.max(largestId, B[i]);
}
rootIndex = A[0];
int res = Integer.MAX_VALUE;
int low = 0, high = Math.min(900, largestId);
while (low <= high) {
int mid = (low + high) / 2;
if (isAvailabel(K, mid)) {
res = Math.min(res, mid);
high = mid - 1;
} else {
low = mid + 1;
}
}
return res;
}
public boolean isAvailabel(int cameraLimit, int notCoveredLength) {
for (int i = 0; i < visited.length; i++) visited[i] = false;
for (int i = 0; i < diameter.length; i++) diameter[i] = -1;
if (dfs(nodeMap[rootIndex], notCoveredLength) > cameraLimit) {
return false;
}
return true;
}
private int dfs(Node node, int limit) {
if (node == null || visited[node.id]) {
return 0;
}
visited[node.id] = true;
int counter = 0;
for (Integer id : node.edgeList) {
counter += dfs(nodeMap[id], limit);
}
/*
@
/ \
@ @(current)
/ \ / \\\
@ @ a b c d
step 1: Sort diameters of subtrees in reverse order, store them in an array
step 2: For each adjacent pair in the sorted array, e.g. [a, b], [b, c], [c, d].
If a + b + 2 > limit, we break the edge connecting current node to a, and counter plus 1.
Do the same for [b, c] and [c, d]
step 3: For last child d. or if current node has only one child.
If d + 1 > limit, we break the edge connecting current node to a, and counter plus 1.
step 4: Set diameter of current node to the largest one of the remains(not broken ones).
*/
/* step 1 */
int n = 0;
for (Integer id : node.edgeList) {
if (diameter[id] != -1) {
sortedSubtreeDiameters[n++] = (diameter[id]);
}
}
Arrays.sort(sortedSubtreeDiameters, 0, n);
for (int i = 0; i < n / 2; i++) { // reverse order
int temp = sortedSubtreeDiameters[i];
sortedSubtreeDiameters[i] = sortedSubtreeDiameters[n - 1 - i];
sortedSubtreeDiameters[n - 1 - i] = temp;
}
/* step 2 */
int maxDiameterAfterRemoval = -1;
for (int i = 0; i < n - 1; i++) {
if (sortedSubtreeDiameters[i] + sortedSubtreeDiameters[i+1] + 2 > limit) {
counter++;
} else {
maxDiameterAfterRemoval = Math.max(maxDiameterAfterRemoval, sortedSubtreeDiameters[i]);
break;
}
}
/* step 3 */
if (n >= 1) {
int i = n - 1;
if (sortedSubtreeDiameters[i] + 1 > limit) {
counter++;
} else {
maxDiameterAfterRemoval = Math.max(maxDiameterAfterRemoval, sortedSubtreeDiameters[i]);
}
}
/* step 4 */
// if subtrees are all removed, we can treat current node as a leaf, so diameter becomes 0
if (maxDiameterAfterRemoval == -1) {
diameter[node.id] = 0;
} else { // if still some subtrees remain, then set current node's diameter as one plus the largest
diameter[node.id] = maxDiameterAfterRemoval + 1;
}
return counter;
}
}
// you can also use imports, for example:
// import java.util.*;
// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");
import java.util.*;
public class Solution {
private int rootIndex = 0;
private int largestId = 0;
private int sortedSubtreeDiameters[] = new int[50001];
private Node nodeMap[] = new Node[50001];
private boolean visited[] = new boolean[50001];
private int diameter[] = new int[50001];
static class Node {
public int id;
public List<Integer> edgeList;
public Node(int v) {
this.id = v;
edgeList = new ArrayList<Integer>();
}
public void addEdge(Integer t) {
edgeList.add(t);
}
}
public int solution(int[] A, int[] B, int K) {
int n = A.length;
for (int i = 0; i < n; i++) {
if (nodeMap[A[i]] == null) {
nodeMap[A[i]] = new Node(A[i]);
}
if (nodeMap[B[i]] == null) {
nodeMap[B[i]] = new Node(B[i]);
}
final Node na = nodeMap[A[i]];
final Node nb = nodeMap[B[i]];
na.addEdge(B[i]);
nb.addEdge(A[i]);
largestId = Math.max(largestId, A[i]);
largestId = Math.max(largestId, B[i]);
}
rootIndex = A[0];
int res = Integer.MAX_VALUE;
int low = 0, high = Math.min(900, largestId);
while (low <= high) {
int mid = (low + high) / 2;
if (isAvailabel(K, mid)) {
res = Math.min(res, mid);
high = mid - 1;
} else {
low = mid + 1;
}
}
return res;
}
public boolean isAvailabel(int cameraLimit, int notCoveredLength) {
for (int i = 0; i < visited.length; i++) visited[i] = false;
for (int i = 0; i < diameter.length; i++) diameter[i] = -1;
if (dfs(nodeMap[rootIndex], notCoveredLength) > cameraLimit) {
return false;
}
return true;
}
private int dfs(Node node, int limit) {
if (node == null || visited[node.id]) {
return 0;
}
visited[node.id] = true;
int counter = 0;
for (Integer id : node.edgeList) {
counter += dfs(nodeMap[id], limit);
}
/*
@
/ \
@ @(current)
/ \ / \\\
@ @ a b c d
step 1: Sort diameters of subtrees in reverse order, store them in an array
step 2: For each adjacent pair in the sorted array, e.g. [a, b], [b, c], [c, d].
If a + b + 2 > limit, we break the edge connecting current node to a, and counter plus 1.
Do the same for [b, c] and [c, d]
step 3: For last child d. or if current node has only one child.
If d + 1 > limit, we break the edge connecting current node to a, and counter plus 1.
step 4: Set diameter of current node to the largest one of the remains(not broken ones).
*/
/* step 1 */
int n = 0;
for (Integer id : node.edgeList) {
if (diameter[id] != -1) {
sortedSubtreeDiameters[n++] = (diameter[id]);
}
}
Arrays.sort(sortedSubtreeDiameters, 0, n);
for (int i = 0; i < n / 2; i++) { // reverse order
int temp = sortedSubtreeDiameters[i];
sortedSubtreeDiameters[i] = sortedSubtreeDiameters[n - 1 - i];
sortedSubtreeDiameters[n - 1 - i] = temp;
}
/* step 2 */
int maxDiameterAfterRemoval = -1;
for (int i = 0; i < n - 1; i++) {
if (sortedSubtreeDiameters[i] + sortedSubtreeDiameters[i+1] + 2 > limit) {
counter++;
} else {
maxDiameterAfterRemoval = Math.max(maxDiameterAfterRemoval, sortedSubtreeDiameters[i]);
break;
}
}
/* step 3 */
if (n >= 1) {
int i = n - 1;
if (sortedSubtreeDiameters[i] + 1 > limit) {
counter++;
} else {
maxDiameterAfterRemoval = Math.max(maxDiameterAfterRemoval, sortedSubtreeDiameters[i]);
}
}
/* step 4 */
// if subtrees are all removed, we can treat current node as a leaf, so diameter becomes 0
if (maxDiameterAfterRemoval == -1) {
diameter[node.id] = 0;
} else { // if still some subtrees remain, then set current node's diameter as one plus the largest
diameter[node.id] = maxDiameterAfterRemoval + 1;
}
return counter;
}
}
The solution obtained perfect score.