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
可能性があり、第1の違いから、我々はゼロ番目の違いでどの要素が未満でないことを要求することにより可能初期値の範囲を推定することができます0
かより大きいn
。
次に、最大の独立集合を見つけます。1つの定理と2つのヒューリスティックを使用しています。
- 定理:グラフの互いに素な結合の最大独立集合は、それらの最大独立集合の結合です。したがって、グラフを接続されていないコンポーネントに分解すると、問題を単純化できます。
- ヒューリスティック:
(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]
いる場合、不平等を理解する方が自然だと思います。