import java.util.*; public class canyon_font { public static void main(String[] args) { new canyon_font(new Scanner(System.in)); } // Determine the minimum side length to cover with 1 square. double testOne(Polygon p) { double dx = p.getMaxX()-p.getMinX(); double dy = p.getMaxY()-p.getMinY(); return Math.max(dx, dy); } // The polygon is split in half, decide which side uses two squares // and which side uses one square. // Determines if it is possible to cover this polygon with K axis aligned // squares where the side len is sideLen. boolean isPossible(Polygon p, int K, double sideLen) { if (p.lines.size() == 0) return true; if (K == 1) return testOne(p) <= sideLen; // Try all the things! if (isPossible(p.clipLowLeft(sideLen), K-1, sideLen)) return true; if (isPossible(p.clipLowRight(sideLen), K-1, sideLen)) return true; if (isPossible(p.clipUpLeft(sideLen), K-1, sideLen)) return true; if (isPossible(p.clipUpRight(sideLen), K-1, sideLen)) return true; if (isPossible(p.clipLeftLow(sideLen), K-1, sideLen)) return true; if (isPossible(p.clipLeftUp(sideLen), K-1, sideLen)) return true; if (isPossible(p.clipRightLow(sideLen), K-1, sideLen)) return true; if (isPossible(p.clipRightUp(sideLen), K-1, sideLen)) return true; return false; } public canyon_font(Scanner in) { int N = in.nextInt(); int K = in.nextInt(); Polygon source = new Polygon(in, N); // Binary Search over the side lengths. double lo = 0.0; double hi = testOne(source); //System.out.printf("[%.5f, %.5f]%n", lo, hi); for (int iter=0; iter<200 && hi-lo > 1e-9; iter++) { double m = (lo+hi)*0.5; if (isPossible(source, K, m)) hi = m; else lo = m; } // Print the minimum side length to 2 decimal places. System.out.printf("%.2f%n", hi); } } // Holds a polygon or fragmented polygon (lines clipped) // It is not possible to form a hole using three boxes. // Due to this, if we are able to clip only lines, it will be // possible to solve the problem by clipping segments. class Polygon { ArrayList lines; public Polygon(Scanner in, int N) { lines = new ArrayList(); vec2[] pts = new vec2[N]; for (int i=0; i(); } double getMinX() { double minX = Long.MAX_VALUE; for (line2 L : lines) minX = Math.min(minX, Math.min(L.a.x, L.b.x)); return minX; } double getMinY() { double minY = Long.MAX_VALUE; for (line2 L : lines) minY = Math.min(minY, Math.min(L.a.y, L.b.y)); return minY; } double getMaxX() { double maxX = Long.MIN_VALUE; for (line2 L : lines) maxX = Math.max(maxX, Math.max(L.a.x, L.b.x)); return maxX; } double getMaxY() { double maxY = Long.MIN_VALUE; for (line2 L : lines) maxY = Math.max(maxY, Math.max(L.a.y, L.b.y)); return maxY; } // Left side = 0, Right side = 1 Polygon[] split(line2 splitLine) { Polygon[] res = new Polygon[2]; res[0] = new Polygon(); res[1] = new Polygon(); for (line2 cur : lines) { vec2 p = cur.intersect(splitLine); if (p == null) { vec2 m = cur.midpoint(); res[splitLine.getSide(m)].lines.add(cur); } else // We have a split, divide the line segment. { int s1 = splitLine.getSide(cur.a); int s2 = splitLine.getSide(cur.b); if (s1 == s2) System.out.printf("BADNESS! Split failed! %s to %s%n", splitLine, cur); res[s1].lines.add(new line2(cur.a, p)); res[s2].lines.add(new line2(cur.b, p)); } } return res; } Polygon merge(Polygon rhs) { Polygon res = new Polygon(); for (line2 L : lines) res.lines.add(L); for (line2 L : rhs.lines) res.lines.add(L); return res; } /******************** CLIP CRAFT ***********************/ Polygon clipLowLeft(double sideLen) { // Split from the bottom double y = getMinY()+sideLen; line2 splitLine = new line2(new vec2(0, y), new vec2(1, y)); Polygon[] sides = split(splitLine); // Split from the left double x = sides[1].getMinX()+sideLen; splitLine = new line2(new vec2(x, 0), new vec2(x, 1)); return sides[0].merge(sides[1].split(splitLine)[1]); } Polygon clipLowRight(double sideLen) { // Split from the bottom double y = getMinY()+sideLen; line2 splitLine = new line2(new vec2(0, y), new vec2(1, y)); Polygon[] sides = split(splitLine); // Split from the right double x = sides[1].getMaxX()-sideLen; splitLine = new line2(new vec2(x, 0), new vec2(x, 1)); return sides[0].merge(sides[1].split(splitLine)[0]); } Polygon clipUpLeft(double sideLen) { // Split from the top double y = getMaxY()-sideLen; line2 splitLine = new line2(new vec2(0, y), new vec2(1, y)); Polygon[] sides = split(splitLine); // Split from the left double x = sides[0].getMinX()+sideLen; splitLine = new line2(new vec2(x, 0), new vec2(x, 1)); return sides[1].merge(sides[0].split(splitLine)[1]); } Polygon clipUpRight(double sideLen) { // Split from the top double y = getMaxY()-sideLen; line2 splitLine = new line2(new vec2(0, y), new vec2(1, y)); Polygon[] sides = split(splitLine); // Split from the right double x = sides[0].getMaxX()-sideLen; splitLine = new line2(new vec2(x, 0), new vec2(x, 1)); return sides[1].merge(sides[0].split(splitLine)[0]); } Polygon clipLeftLow(double sideLen) { // Split from the left double x = getMinX()+sideLen; line2 splitLine = new line2(new vec2(x, 0), new vec2(x, 1)); Polygon[] sides = split(splitLine); // Split from the bottom double y = sides[0].getMinY()+sideLen; splitLine = new line2(new vec2(0, y), new vec2(1, y)); return sides[1].merge(sides[0].split(splitLine)[0]); } Polygon clipLeftUp(double sideLen) { // Split from the left double x = getMinX()+sideLen; line2 splitLine = new line2(new vec2(x, 0), new vec2(x, 1)); Polygon[] sides = split(splitLine); // Split from the top double y = sides[0].getMaxY()-sideLen; splitLine = new line2(new vec2(0, y), new vec2(1, y)); return sides[1].merge(sides[0].split(splitLine)[1]); } Polygon clipRightLow(double sideLen) { // Split from the right double x = getMaxX()-sideLen; line2 splitLine = new line2(new vec2(x, 0), new vec2(x, 1)); Polygon[] sides = split(splitLine); // Split from the bottom double y = sides[1].getMinY()+sideLen; splitLine = new line2(new vec2(0, y), new vec2(1, y)); return sides[0].merge(sides[1].split(splitLine)[0]); } Polygon clipRightUp(double sideLen) { // Split from the right double x = getMaxX()-sideLen; line2 splitLine = new line2(new vec2(x, 0), new vec2(x, 1)); Polygon[] sides = split(splitLine); // Split from the top double y = sides[1].getMaxY()-sideLen; splitLine = new line2(new vec2(0, y), new vec2(1, y)); return sides[0].merge(sides[1].split(splitLine)[1]); } /**************** END CLIP CRAFT ***********************/ } class line2 { vec2 a, b; public line2(vec2 aa, vec2 bb) { a=aa; b=bb; } vec2 getVec2() { return b.sub(a); } // Returns 0 if the point is on the left side, 1 otherwise. int getSide(vec2 p) { vec2 v1 = getVec2(), v2 = p.sub(a); return v1.cross(v2) > 0 ? 0 : 1; } vec2 midpoint() { return a.midpoint(b); } // Line to Line segment intersection, this is a line, rhs is a segment. // The function excludes endpoints on the segment. vec2 intersect(line2 rhs) { vec2 v1 = getVec2(), v2 = rhs.getVec2(), v3 = rhs.a.sub(a); double div = v1.cross(v2), sn = v3.cross(v2); //System.out.printf("cross = %.6f%n", div); if (Math.abs(div) < 1e-9) return null; double s = sn/div; vec2 p = v1.scale(s).add(a); double ZERO = 1e-9, ONE = 1-1e-9; //System.out.printf("HAPPENS s=%.20f%n", s); if (s < ZERO || s > ONE) return null; return p; } public String toString() { return String.format("(%s, %s)", a, b); } } class vec2 { double x, y; public vec2(double xx, double yy) { x=xx; y=yy; } vec2 add(vec2 rhs) { return new vec2(x+rhs.x, y+rhs.y); } vec2 sub(vec2 rhs) { return new vec2(x-rhs.x, y-rhs.y); } vec2 midpoint(vec2 rhs) { return new vec2(0.5*(x+rhs.x), 0.5*(y+rhs.y)); } vec2 scale(double s) { return new vec2(x*s, y*s); } double cross(vec2 rhs) { return x*rhs.y-y*rhs.x; } public String toString() { return String.format("<%.6f, %.6f>", x, y); } }