/** * Ghostbusters: Steven Zeil */ import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.Scanner; public class Ghostbusters_sjz { Scanner input; static final int maxXY = 1000000; public Ghostbusters_sjz(BufferedReader input) { this.input = new Scanner(input); } public static void main(String[] args) throws FileNotFoundException, IOException { if (args.length > 0) { for (int i = 0; i < args.length; ++i) { try (BufferedReader input = new BufferedReader(new FileReader(args[i]))) { new Ghostbusters_sjz(input).solve(); } } } else { BufferedReader input = new BufferedReader (new InputStreamReader(System.in)); new Ghostbusters_sjz(input).solve(); } } private class ProtonPack { int index; int x; int y; boolean isHorizontal; int root; boolean visited; List inEdges; List outEdges; ProtonPack () { index = 0; x = 0; y = 0; isHorizontal = true; root = -1; visited = false; inEdges = new LinkedList(); outEdges = new LinkedList(); } ProtonPack (int i, int xx, int yy, boolean horizontal) { index = i; x = xx; y = yy; isHorizontal = horizontal; root = -1; visited = false; inEdges = new LinkedList(); outEdges = new LinkedList(); } boolean conflictsWith(ProtonPack pack, int range) { if (isHorizontal == pack.isHorizontal) { if (isHorizontal) { // Both packs are horizontal. return (y == pack.y) && (Math.abs(x - pack.x) <= 2*range); } else { // both packs are vertical return (x == pack.x) && (Math.abs(y - pack.y) <= 2*range); } } else { return false; } } void implies (ProtonPack pack) { if (index != pack.index) { //System.err.println("" + index + ' ' + toString() + " implies " + pack.index + ' ' + pack); outEdges.add(pack.index); pack.inEdges.add(index); } } public String toString() { return "(" + x + ',' + y + ',' + ((isHorizontal) ? 'H' : 'V') + ')'; } // Kosaaju's algorithm for findinc strongly connected components void visit(LinkedList L) { /* if (!visited) { visited = true; for (int out: outEdges) { allPacks[out].visit(L); } L.addFirst(index); } */ LinkedList toBeVisited = new LinkedList<>(); toBeVisited.add(index); while (!toBeVisited.isEmpty()) { int index = toBeVisited.getFirst(); toBeVisited.removeFirst(); if (index < 0) { L.addFirst(-index); } else { ProtonPack pack = allPacks[index]; if (!pack.visited) { pack.visited = true; toBeVisited.addFirst(-index); //System.err.println("Visited " + lamp.index + ' ' + lamp.toString()); for (int out: pack.outEdges) { toBeVisited.addFirst(out); } } } } } void assign (int componentRoot) { if (root < 0) { root = componentRoot; for (int in: inEdges) { allPacks[in].assign(componentRoot); } } /* LinkedList toBeAssigned = new LinkedList<>(); toBeAssigned.add(index); while (!toBeAssigned.isEmpty()) { ProtonPack pack = allPacks[toBeAssigned.getFirst()]; toBeAssigned.removeFirst(); if (pack.root < 0) { //System.err.println("Assigning " + pack.index + ' ' + pack.toString() + " to " + componentRoot); pack.root = componentRoot; for (int in: pack.inEdges) { toBeAssigned.addLast(in); } } } */ } }; int L; ProtonPack[] allPacks; ProtonPack[] byX; ProtonPack[] byY; ProtonPack alternate (ProtonPack pack) { if (pack.index % 2 == 0) return allPacks[pack.index + 1]; else return allPacks[pack.index - 1]; } void readLamps() { for (int i = 0; i < L; ++i) { int x = input.nextInt(); int y = input.nextInt(); allPacks[2*i] = new ProtonPack(2*i, x, y, false); allPacks[2*i+1] = new ProtonPack(2*i+1, x, y, true); } byX = new ProtonPack[2*L]; byY = new ProtonPack[2*L]; for (int i = 0; i < 2*L; ++i) { byX[i] = allPacks[i]; byY[i] = allPacks[i]; } Arrays.sort(byX, new ComparePacksByX()); Arrays.sort(byY, new ComparePacksByY()); } class ComparePacksByX implements Comparator { @Override public int compare(ProtonPack left, ProtonPack right) { if (left.x < right.x) return -1; else if (left.x > right.x) return 1; else { if (left.y < right.y) return -1; else if (left.y > right.y) return 1; else return 0; } } } class ComparePacksByY implements Comparator { @Override public int compare(ProtonPack left, ProtonPack right) { if (left.y < right.y) return -1; else if (left.y > right.y) return 1; else { if (left.x < right.x) return -1; else if (left.x > right.x) return 1; else return 0; } } } void findConflicts(int R) { for (int ipack1 = 0; ipack1 < 2*L; ++ipack1) { ProtonPack pack1 = allPacks[ipack1]; pack1.root = -1; pack1.visited = false; pack1.inEdges.clear(); pack1.outEdges.clear(); } for (int i = 0; i < byX.length; ++i) { for (int j = i + 1; j < byX.length && byX[i].x == byX[j].x && byX[i].y + 2*R >= byX[j].y; ++j) { if (byX[i].conflictsWith(byX[j], R)) { ProtonPack pack1 = byX[i]; ProtonPack pack2 = byX[j]; ProtonPack pack1B = alternate(pack1); ProtonPack pack2B = alternate(pack2); // If we choose pack 1, we must also choose 2b // because 1 and 2 conflict. /* if (pack1.index < pack2.index) System.out.println("Conflict between " + pack1 + " and " + pack2); else System.out.println("Conflict between " + pack2 + " and " + pack1); */ pack1.implies (pack2B); pack2.implies (pack1B); } } } for (int i = 0; i < byY.length; ++i) { for (int j = i + 1; j < byY.length && byY[i].y == byY[j].y && byY[i].x + 2*R >= byY[j].x; ++j) { if (byY[i].conflictsWith(byY[j], R)) { ProtonPack pack1 = byY[i]; ProtonPack pack2 = byY[j]; ProtonPack pack1B = alternate(pack1); ProtonPack pack2B = alternate(pack2); // If we choose pack 1, we must also choose 2b // because 1 and 2 conflict. /* if (pack1.index < pack2.index) System.out.println("Conflict between " + pack1 + " and " + pack2); else System.out.println("Conflict between " + pack2 + " and " + pack1); */ pack1.implies (pack2B); pack2.implies (pack1B); } } } } void analyze() { LinkedList visiters = new LinkedList<>(); for (int ipack1 = 0; ipack1 < 2*L; ++ipack1) { ProtonPack pack1 = allPacks[ipack1]; pack1.visit(visiters); } for (int ipack: visiters) { ProtonPack pack = allPacks[ipack]; //System.err.println("Assigning " + ipack + ' ' + pack); pack.assign(ipack); } } boolean hasASolution() { boolean conflict = false; for (int i = 0; (!conflict) && i < L; ++i) { int pack1V = 2*i; int pack1H = 2*i+1; int root1 = allPacks[pack1V].root; int root2 = allPacks[pack1H].root; //System.err.println("" + 2*i + ':' + root1 + " " + 2*i+1 + ':' + root2); if (root1 == root2) { // We need a pack to be simultaneously horizontal and vertical conflict = true; } } return !conflict; } void solve() { L = input.nextInt(); allPacks = new ProtonPack[2*L]; readLamps(); int Rmax = 1; int Rmin = 0; while (Rmax < maxXY && solvable(Rmax)) { Rmin = Rmax; Rmax *= 2; } if (Rmax >= maxXY) { System.out.println("UNLIMITED"); } else { while (Rmax > Rmin) { int R = (Rmax + Rmin + 1) / 2; if (solvable(R)) { Rmin = R; } else { Rmax = R-1; } } System.out.println(Rmin); } } boolean solvable (int R) { findConflicts(R); analyze(); return hasASolution(); } }