Welcome to FutureAppLaboratory

v=(*^ワ^*)=v

Codility Calcium 2015

| Comments

Introduction

This is an solution in java of Codility Calcium 2015.

You can find problem definition here. https://codility.com/programmers/challenges/

The key logic is using binary search to limit the answer area.

For each iteration of binary search, detail steps are written in the following code.

Optimization for 100% solution

This problem is very time strict for java.

For large data set, my old answer which use many Collections always runs for 6+ seconds, but time limit is only 4 seconds.

So I replace almost all of them with array in heavy calculation part, to reduce time consuming of GC.

This results an 3+ seconds for each iteration of binary search.

https://codility.com/cert/view/certXKYPW7-D87MFQHQEGCKWRV9/details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import java.util.*;

public class TrafficCamera {

    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). Note that, if (a, b) passed the test, we don't need to anymore
            because (a, b) is the largest pair of them.
         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; // Even the largest pair is in the limit, we don't need check the remaining pairs.
            }
        }

        /* 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;
    }

}

Comments