281. Java 5, 11628 byte, A000947
// package oeis_challenge;
import java.util.*;
import java.lang.*;
class Main {
// static void assert(boolean cond) {
// if (!cond)
// throw new Error("Assertion failed!");
// }
/* Use the formula a(n) = A000063(n + 2) - A000936(n).
It's unfair that I use the formula of "number of free polyenoid with n
nodes and symmetry point group C_{2v}" (formula listed in A000063)
without understanding why it's true...
*/
static int catalan(int x) {
int ans = 1;
for (int i = 1; i <= x; ++i)
ans = ans * (2*x+1-i) / i;
return ans / -~x;
}
static int A63(int n) {
int ans = catalan(n/2 - 1);
if (n%4 == 0) ans -= catalan(n/4 - 1);
if (n%6 == 0) ans -= catalan(n/6 - 1);
return ans;
}
static class Point implements Comparable<Point> {
final int x, y;
Point(int _x, int _y) {
x = _x; y = _y;
}
/// @return true if this is a point, false otherwise (this is a vector)
public boolean isPoint() {
return (x + y) % 3 != 0;
}
/// Translate this point by a vector.
public Point add(Point p) {
assert(this.isPoint() && ! p.isPoint());
return new Point(x + p.x, y + p.y);
}
/// Reflect this point along x-axis.
public Point reflectX() {
return new Point(x - y, -y);
}
/// Rotate this point 60 degrees counter-clockwise.
public Point rot60() {
return new Point(x - y, x);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Point)) return false;
Point p = (Point) o;
return x == p.x && y == p.y;
}
@Override
public int hashCode() {
return 21521 * (3491 + x) + y;
}
public String toString() {
// return String.format("(%d, %d)", x, y);
return String.format("setxy %d %d", x * 50 - y * 25, y * 40);
}
public int compareTo(Point p) {
int a = Integer.valueOf(x).compareTo(p.x);
if (a != 0) return a;
return Integer.valueOf(y).compareTo(p.y);
}
/// Helper class.
static interface Predicate {
abstract boolean test(Point p);
}
static abstract class UnaryFunction {
abstract Point apply(Point p);
}
}
static class Edge implements Comparable<Edge> {
final Point a, b; // guarantee a < b
Edge(Point x, Point y) {
assert x != y;
if (x.compareTo(y) > 0) { // y < x
a = y; b = x;
} else {
a = x; b = y;
}
}
public int compareTo(Edge e) {
int x = a.compareTo(e.a);
if (x != 0) return x;
return b.compareTo(e.b);
}
}
/// A graph consists of multiple {@code Point}s.
static class Graph {
private HashMap<Point, Point> points;
public Graph() {
points = new HashMap<Point, Point>();
}
public Graph(Graph g) {
points = new HashMap<Point, Point>(g.points);
}
public void add(Point p, Point root) {
assert(p.isPoint());
assert(root.isPoint());
assert(p == root || points.containsKey(root));
points.put(p, root);
}
public Graph map(Point.UnaryFunction fn) {
Graph result = new Graph();
for (Map.Entry<Point, Point> pq : points.entrySet()) {
Point p = pq.getKey(), q = pq.getValue();
assert(p.isPoint()) : p;
assert(q.isPoint()) : q;
p = fn.apply(p); assert(p.isPoint()) : p;
q = fn.apply(q); assert(q.isPoint()) : q;
result.points.put(p, q);
}
return result;
}
public Graph reflectX() {
return this.map(new Point.UnaryFunction() {
public Point apply(Point p) {
return p.reflectX();
}
});
}
public Graph rot60() {
return this.map(new Point.UnaryFunction() {
public Point apply(Point p) {
return p.rot60();
}
});
}
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (o.getClass() != getClass()) return false;
Graph g = (Graph) o;
return points.equals(g.points);
}
@Override
public int hashCode() {
return points.hashCode();
}
Graph[] expand(Point.Predicate fn) {
List<Graph> result = new ArrayList<Graph>();
for (Point p : points.keySet()) {
int[] deltaX = new int[] { -1, 0, 1, 1, 0, -1};
int[] deltaY = new int[] { 0, 1, 1, 0, -1, -1};
for (int i = 6; i --> 0;) {
Point p1 = new Point(p.x + deltaX[i], p.y + deltaY[i]);
if (points.containsKey(p1) || !fn.test(p1)
|| !p1.isPoint()) continue;
Graph g = new Graph(this);
g.add(p1, p);
result.add(g);
}
}
return result.toArray(new Graph[0]);
}
public static Graph[] expand(Graph[] graphs, Point.Predicate fn) {
Set<Graph> result = new HashSet<Graph>();
for (Graph g0 : graphs) {
Graph[] g = g0.expand(fn);
for (Graph g1 : g) {
if (result.contains(g1)) continue;
result.add(g1);
}
}
return result.toArray(new Graph[0]);
}
private Edge[] edges() {
List<Edge> result = new ArrayList<Edge>();
for (Map.Entry<Point, Point> pq : points.entrySet()) {
Point p = pq.getKey(), q = pq.getValue();
if (p.equals(q)) continue;
result.add(new Edge(p, q));
}
return result.toArray(new Edge[0]);
}
/**
* Check if two graphs are isomorphic... under translation.
* @return {@code true} if {@code this} is isomorphic
* under translation, {@code false} otherwise.
*/
public boolean isomorphic(Graph g) {
if (points.size() != g.points.size()) return false;
Edge[] a = this.edges();
Edge[] b = g.edges();
Arrays.sort(a);
Arrays.sort(b);
// for (Edge e : b)
// System.err.println(e.a + " - " + e.b);
// System.err.println("------- >><< ");
assert (a.length > 0);
assert (a.length == b.length);
int a_bx = a[0].a.x - b[0].a.x, a_by = a[0].a.y - b[0].a.y;
for (int i = 0; i < a.length; ++i) {
if (a_bx != a[i].a.x - b[i].a.x ||
a_by != a[i].a.y - b[i].a.y) return false;
if (a_bx != a[i].b.x - b[i].b.x ||
a_by != a[i].b.y - b[i].b.y) return false;
}
return true;
}
// C_{2v}.
public boolean correctSymmetry() {
Graph[] graphs = new Graph[6];
graphs[0] = this.reflectX();
for (int i = 1; i < 6; ++i) graphs[i] = graphs[i-1].rot60();
assert(graphs[5].rot60().isomorphic(graphs[0]));
int count = 0;
for (Graph g : graphs) {
if (this.isomorphic(g)) ++count;
// if (count >= 2) {
// return false;
// }
}
// if (count > 1) System.err.format("too much: %d%n", count);
assert(count > 0);
return count == 1; // which is, basically, true
}
public void reflectSelfType2() {
Graph g = this.map(new Point.UnaryFunction() {
public Point apply(Point p) {
return new Point(p.y - p.x, p.y);
}
});
Point p = new Point(1, 1);
assert (p.equals(points.get(p)));
points.putAll(g.points);
assert (p.equals(points.get(p)));
Point q = new Point(0, 1);
assert (q.equals(points.get(q)));
points.put(p, q);
}
public void reflectSelfX() {
Graph g = this.reflectX();
points.putAll(g.points); // duplicates doesn't matter
}
}
static int A936(int n) {
// if (true) return (new int[]{0, 0, 0, 1, 1, 2, 4, 4, 12, 10, 29, 27, 88, 76, 247, 217, 722, 638, 2134, 1901, 6413})[n];
// some unreachable codes here for testing.
int ans = 0;
if (n % 2 == 0) { // reflection type 2. (through line 2x == y)
Graph[] graphs = new Graph[1];
graphs[0] = new Graph();
Point p = new Point(1, 1);
graphs[0].add(p, p);
for (int i = n / 2 - 1; i --> 0;)
graphs = Graph.expand(graphs, new Point.Predicate() {
public boolean test(Point p) {
return 2*p.x > p.y;
}
});
int count = 0;
for (Graph g : graphs) {
g.reflectSelfType2();
if (g.correctSymmetry()) {
++count;
// for (Edge e : g.edges())
// System.err.println(e.a + " - " + e.b);
// System.err.println("------*");
}
// else System.err.println("Failed");
}
assert (count%2 == 0);
// System.err.println("A936(" + n + ") count = " + count + " -> " + (count/2));
ans += count / 2;
}
// Reflection type 1. (reflectX)
Graph[] graphs = new Graph[1];
graphs[0] = new Graph();
Point p = new Point(1, 0);
graphs[0].add(p, p);
if (n % 2 == 0) graphs[0].add(new Point(2, 0), p);
for (int i = (n-1) / 2; i --> 0;)
graphs = Graph.expand(graphs, new Point.Predicate() {
public boolean test(Point p) {
return p.y > 0;
}
});
int count = 0;
for (Graph g : graphs) {
g.reflectSelfX();
if (g.correctSymmetry()) {
++count;
// for (Edge e : g.edges())
// System.err.printf(
// "pu %s pd %s\n"
// // "%s - %s%n"
// , e.a, e.b);
// System.err.println("-------/");
}
// else System.err.println("Failed");
}
if(n % 2 == 0) {
assert(count % 2 == 0);
count /= 2;
}
ans += count;
// System.err.println("A936(" + n + ") = " + ans);
return ans;
}
public static void main(String[] args) {
// Probably
if (! "1.5.0_22".equals(System.getProperty("java.version"))) {
System.err.println("Warning: Java version is not 1.5.0_22");
}
// A936(6);
for (int i = 0; i < 20; ++i)
System.out.println(i + " | " + (A63(i+9) - A936(i+7)));
//A936(i+2);
}
}
Hãy thử trực tuyến!
Lưu ý bên:
- Đã thử nghiệm cục bộ với Java 5. (sao cho cảnh báo không được in - xem tab gỡ lỗi TIO)
- Đừng. Không bao giờ. Sử dụng. Java. 1. Nó dài dòng hơn Java nói chung.
Điều này có thể phá vỡ chuỗi.
- Khoảng cách (7 ngày và 48 phút) không nhiều hơn khoảng cách được tạo bởi câu trả lời này , tức là 7 ngày và 1 giờ 25 phút so với trước đó .
Kỷ lục mới về số lượng lớn! Bởi vì tôi (nhầm lẫn?) Sử dụng khoảng trắng thay vì các tab, nên bytecount lớn hơn mức cần thiết. Trên máy của tôi, nó là 9550 byte. (tại thời điểm viết bản sửa đổi này)
- Trình tự tiếp theo .
- Mã, ở dạng hiện tại, chỉ in 20 điều khoản đầu tiên của chuỗi. Tuy nhiên thật dễ dàng để thay đổi để nó sẽ in 1.000 mặt hàng đầu tiên (bằng cách thay đổi các
20
trong for (int i = 0; i < 20; ++i)
để 1000
)
Yay! Điều này có thể tính toán nhiều điều khoản hơn được liệt kê trên trang OEIS! (lần đầu tiên, đối với một thách thức tôi cần sử dụng Java) trừ khi OEIS có nhiều điều khoản hơn ở đâu đó ...
Giải thích nhanh
Giải thích về mô tả trình tự.
Trình tự yêu cầu số lượng polyeno không phẳng miễn phí với nhóm đối xứng C 2v , trong đó:
- polyenoid: (mô hình toán học của polyene hydrocarbon) (hoặc trong trường hợp suy biến, đỉnh đơn) với có thể được nhúng trong mạng lục giác.
Ví dụ, hãy xem xét các cây
O O O O (3)
| \ / \
| \ / \
O --- O --- O O --- O O --- O
| \
| (2) \
(1) O O
Cái đầu tiên không thể được nhúng trong mạng lục giác, trong khi cái thứ hai có thể. Sự nhúng đặc biệt đó được coi là khác với cây thứ ba.
- nonplanar polyenoid: nhúng cây sao cho tồn tại hai đỉnh chồng chéo.
(2)
và (3)
cây ở trên là phẳng. Điều này, tuy nhiên, là phi kế hoạch:
O---O O
/ \
/ \
O O
\ /
\ /
O --- O
(có 7 đỉnh và 6 cạnh)
- polyen tự do: Các biến thể của một polyen, có thể thu được bằng cách quay và phản xạ, được tính là một.
- Nhóm C 2v : Đa giác chỉ được tính nếu chúng có 2 mặt phẳng vuông góc phản xạ, và không còn nữa.
Ví dụ: đa giác duy nhất có 2 đỉnh
O --- O
có 3 mặt phẳng phản xạ: Mặt phẳng ngang -
, mặt phẳng dọc |
và mặt phẳng song song với màn hình máy tính ■
. Qua nhiều.
Mặt khác, cái này
O --- O
\
\
O
có 2 mặt phẳng phản xạ: /
và ■
.
Giải thích về phương pháp
Và bây giờ, cách tiếp cận về cách thực sự đếm số.
Đầu tiên, tôi dùng công thức a(n) = A000063(n + 2) - A000936(n)
(được liệt kê trên trang OEIS) để được cấp. Tôi đã không đọc lời giải thích trong bài báo.
[TODO sửa phần này]
Tất nhiên, đếm phẳng dễ hơn đếm phi phẳng. Đó cũng là những gì tờ báo làm.
Các polyenoids phẳng hình học (không có các đỉnh chồng chéo) được liệt kê bằng lập trình máy tính. Do đó, số lượng polyenoids phi hình học trở nên dễ tiếp cận.
Vì vậy, ... chương trình sẽ đếm số lượng đa cực phẳng và trừ nó ra khỏi tổng số.
Vì dù sao cây cũng là mặt phẳng, nên rõ ràng nó có ■
mặt phẳng phản xạ. Vì vậy, điều kiện sôi xuống là "đếm số lượng cây có trục phản xạ trong biểu diễn 2D của nó".
Cách ngây thơ sẽ tạo ra tất cả các cây với n
các nút và kiểm tra tính đối xứng chính xác. Tuy nhiên, vì chúng tôi chỉ muốn tìm số lượng cây có trục phản xạ, chúng tôi chỉ có thể tạo tất cả nửa cây có thể trên một nửa, phản chiếu chúng qua trục và sau đó kiểm tra tính đối xứng chính xác. Hơn nữa, vì các polyenoids được tạo ra là cây (phẳng), nên nó phải chạm vào trục phản xạ chính xác một lần.
Hàm public static Graph[] expand(Graph[] graphs, Point.Predicate fn)
lấy một mảng đồ thị, mỗi có n
nút và xuất ra một mảng đồ thị, mỗi n+1
nút có các nút, không bằng nhau (dưới bản dịch) - sao cho nút được thêm phải thỏa mãn vị ngữ fn
.
Xem xét 2 trục phản xạ có thể có: Một trục đi qua một đỉnh và trùng với các cạnh ( x = 0
) và một trục là đường phân giác vuông góc của một cạnh ( 2x = y
). Dù sao, chúng ta chỉ có thể lấy một trong số chúng vì các biểu đồ được tạo là đẳng cấu.
Vì vậy, đối với trục đầu tiên x = 0
, chúng ta bắt đầu từ biểu đồ cơ sở bao gồm một nút đơn (1, 0)
(trong trường hợp n
là số lẻ) hoặc hai nút có cạnh giữa (1, 0) - (2, 0)
(trong trường hợp n
là chẵn), sau đó mở rộng các nút sao cho y > 0
. Điều đó được thực hiện bởi phần "Reflection type 1" của chương trình, và sau đó với mỗi biểu đồ được tạo, hãy phản chiếu (gương) qua trục X x = 0
( g.reflectSelfX()
), sau đó kiểm tra xem nó có đối xứng chính xác không.
Tuy nhiên, lưu ý rằng nếu n
chia hết cho 2, bằng cách này, chúng tôi đã đếm từng biểu đồ hai lần, bởi vì chúng tôi cũng tạo ra hình ảnh phản chiếu của nó theo trục 2x = y + 3
.
(lưu ý 2 quả cam)
Tương tự cho các trục 2x = y
, nếu (và chỉ nếu) n
thậm chí còn, chúng tôi bắt đầu từ điểm (1, 1)
, tạo các biểu đồ như vậy 2*x > y
, và phản ánh mỗi người trong 2x = y
trục ( g.reflectSelfType2()
), kết nối (1, 0)
với (1, 1)
, và kiểm tra xem họ có đối xứng chính xác. Nhớ chia cho 2, quá.