2,4,10,16,31,47,76,111,166,235
笔记
如果我们考虑的图形G
与顶点0
到n
和边缘连接两个数字哪个匹配,则张电源 G^n
具有顶点(x_0, ..., x_{n-1})
形成直角功率{0, ..., n}^n
和匹配的元组之间的边缘。感兴趣的图是由对应于可能的“计数阵列”的那些顶点G^n
诱导的子图。
因此,第一个子任务是生成这些顶点。天真的方法枚举2^{2n-1}
字符串,或按顺序4^n
。但是,如果我们改为查看计数数组的一阶差的数组,则会发现只有3^n
可能性,并且可以通过要求零阶差中的任何元素都不小于0
或等于,从一阶差推导出可能的初始值的范围。大于n
。
然后,我们想找到最大的独立集。我正在使用一个定理和两种启发式方法:
- 定理:图的不相交联合的最大独立集是其最大独立集的并集。因此,如果我们将图形分解为未连接的组件,则可以简化问题。
- 启发式:假定
(n, n, ..., n)
将在最大的独立集合中。有相当多的顶点团,{m, m+1, ..., n}^n
其中m
最小的整数匹配n
;(n, n, ..., n)
保证在该派系之外没有任何比赛。
- 启发式:采用贪婪的方法来选择最低度的顶点。
在我的电脑这个认定111
为n=8
16秒,166
为n=9
8分钟左右,并235
为n=10
在约2小时。
码
另存为PPCG54354.java
,编译为javac PPCG54354.java
并运行为java PPCG54354
。
import java.util.*;
public class PPCG54354 {
public static void main(String[] args) {
for (int n = 1; n < 20; n++) {
long start = System.nanoTime();
Set<Vertex> constructive = new HashSet<Vertex>();
for (int i = 0; i < (int)Math.pow(3, n-1); i++) {
int min = 0, max = 1, diffs[] = new int[n-1];
for (int j = i, k = 0; k < n-1; j /= 3, k++) {
int delta = (j % 3) - 1;
if (delta == -1) min++;
if (delta != 1) max++;
diffs[k] = delta;
}
for (; min <= max; min++) constructive.add(new Vertex(min, diffs));
}
// Heuristic: favour (n, n, ..., n)
Vertex max = new Vertex(n, new int[n-1]);
Iterator<Vertex> it = constructive.iterator();
while (it.hasNext()) {
Vertex v = it.next();
if (v.matches(max) && !v.equals(max)) it.remove();
}
Set<Vertex> ind = independentSet(constructive, n);
System.out.println(ind.size() + " after " + ((System.nanoTime() - start) / 1000000000L) + " secs");
}
}
private static Set<Vertex> independentSet(Set<Vertex> vertices, int dim) {
if (vertices.size() < 2) return vertices;
for (int idx = 0; idx < dim; idx++) {
Set<Set<Vertex>> p = connectedComponents(vertices, idx);
if (p.size() > 1) {
Set<Vertex> ind = new HashSet<Vertex>();
for (Set<Vertex> part : connectedComponents(vertices, idx)) {
ind.addAll(independentSet(part, dim));
}
return ind;
}
}
// Greedy
int minMatches = Integer.MAX_VALUE;
Vertex minV = null;
for (Vertex v0 : vertices) {
int numMatches = 0;
for (Vertex vi : vertices) if (v0.matches(vi)) numMatches++;
if (numMatches < minMatches) {
minMatches = numMatches;
minV = v0;
}
}
Set<Vertex> nonmatch = new HashSet<Vertex>();
for (Vertex vi : vertices) if (!minV.matches(vi)) nonmatch.add(vi);
Set<Vertex> ind = independentSet(nonmatch, dim);
ind.add(minV);
return ind;
}
// Separates out a set of vertices which form connected components when projected into the idx axis.
private static Set<Set<Vertex>> connectedComponents(Set<Vertex> vertices, final int idx) {
List<Vertex> sorted = new ArrayList<Vertex>(vertices);
Collections.sort(sorted, new Comparator<Vertex>() {
public int compare(Vertex a, Vertex b) {
return a.x[idx] - b.x[idx];
}
});
Set<Set<Vertex>> connectedComponents = new HashSet<Set<Vertex>>();
Set<Vertex> current = new HashSet<Vertex>();
int currentVal = 0;
for (Vertex v : sorted) {
if (!match(currentVal, v.x[idx]) && !current.isEmpty()) {
connectedComponents.add(current);
current = new HashSet<Vertex>();
}
current.add(v);
currentVal = v.x[idx];
}
if (!current.isEmpty()) connectedComponents.add(current);
return connectedComponents;
}
private static boolean match(int a, int b) {
return a <= 2 * b && b <= 2 * a;
}
private static class Vertex {
final int[] x;
private final int h;
Vertex(int[] x) {
this.x = x.clone();
int _h = 0;
for (int xi : x) _h = _h * 37 + xi;
h = _h;
}
Vertex(int x0, int[] diffs) {
x = new int[diffs.length + 1];
x[0] = x0;
for (int i = 0; i < diffs.length; i++) x[i+1] = x[i] + diffs[i];
int _h = 0;
for (int xi : x) _h = _h * 37 + xi;
h = _h;
}
public boolean matches(Vertex v) {
if (v == this) return true;
if (x.length != v.x.length) throw new IllegalArgumentException("v");
for (int i = 0; i < x.length; i++) {
if (!match(x[i], v.x[i])) return false;
}
return true;
}
@Override
public int hashCode() {
return h;
}
@Override
public boolean equals(Object obj) {
return (obj instanceof Vertex) && equals((Vertex)obj);
}
public boolean equals(Vertex v) {
if (v == this) return true;
if (x.length != v.x.length) return false;
for (int i = 0; i < x.length; i++) {
if (x[i] != v.x[i]) return false;
}
return true;
}
@Override
public String toString() {
if (x.length == 0) return "e";
StringBuilder sb = new StringBuilder(x.length);
for (int xi : x) sb.append(xi < 10 ? (char)('0' + xi) : (char)('A' + xi - 10));
return sb.toString();
}
}
}
L1[i]/2 <= L2[i] <= 2*L1[i]
。