Skip to content

Commit 516f777

Browse files
author
Takanori MAEHARA
authored
Fisher, Kasteleyn, and Temperley algorithm for counting perfect matchings in plane graph
1 parent d341562 commit 516f777

File tree

1 file changed

+196
-0
lines changed

1 file changed

+196
-0
lines changed

graph/plane_perfect_matchings.cc

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
//
2+
// Counting Perfect Matchings in Plane Graph
3+
// (Fisher, Kasteleyn, and Temperley)
4+
//
5+
// Description:
6+
//
7+
// Pfaffian Orientation; see https://en.wikipedia.org/wiki/FKT_algorithm
8+
//
9+
// Complexity:
10+
//
11+
// O(n^3).
12+
//
13+
// g++ -std=c++17 -O3 -fmax-errors=1 -fsanitize=undefined
14+
#include <bits/stdc++.h>
15+
16+
using namespace std;
17+
18+
#define fst first
19+
#define snd second
20+
#define all(c) ((c).begin()), ((c).end())
21+
#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); }
22+
23+
using Real = long double;
24+
struct Point {
25+
Real x, y;
26+
};
27+
28+
struct PlaneGraph {
29+
vector<int> incident_edge; // vertex record
30+
vector<int> origin, twin, prev, next, incident_face; // edge record
31+
vector<int> component; // face record
32+
int edges() const { return origin.size(); }
33+
int vertices() const { return incident_edge.size(); }
34+
int faces() const { return component.size(); }
35+
36+
vector<Point> point;
37+
int newVertex(Point p, int e = -1) {
38+
point.push_back(p);
39+
incident_edge.push_back(e);
40+
return vertices()-1;
41+
}
42+
int newEdge(int o = -1) {
43+
origin.push_back(o);
44+
twin.push_back(-1);
45+
prev.push_back(-1);
46+
next.push_back(-1);
47+
incident_face.push_back(-1);
48+
return edges()-1;
49+
}
50+
int newFace(int e = -1) {
51+
component.push_back(e);
52+
return component.size()-1;
53+
}
54+
void completeFaces() {
55+
component.clear();
56+
fill(all(incident_face), -1);
57+
for (int e = 0; e < edges(); ++e) {
58+
if (incident_face[e] >= 0) continue;
59+
int f = newFace(e), x = e;
60+
do {
61+
incident_face[x] = f;
62+
x = next[x];
63+
} while (x != e);
64+
}
65+
}
66+
67+
// assume connected
68+
vector<int> pfaffianOrientation() {
69+
// take any spanning tree T
70+
vector<int> dir(edges(), -2), seen(vertices());
71+
function<void(int)> dfs1 = [&](int u) {
72+
seen[u] = 1;
73+
int e = incident_edge[u];
74+
do {
75+
int v = origin[twin[e]];
76+
if (!seen[v]) {
77+
dir[e] = 1;
78+
dir[twin[e]] = -dir[e];
79+
dfs1(v);
80+
}
81+
e = next[twin[e]];
82+
} while (e != incident_edge[u]);
83+
};
84+
for (int u = 0; u < vertices(); ++u)
85+
if (!seen[u]) dfs1(u);
86+
87+
// take any dual spanning tree that does not cross T
88+
seen = vector<int>(faces());
89+
vector<int> come(faces(), -1);
90+
function<void(int,int)> dfs2 = [&](int f, int p) {
91+
int parity = 0;
92+
int free_edge = -1;
93+
seen[f] = 1;
94+
int e = component[f];
95+
do {
96+
int g = incident_face[twin[e]];
97+
if (dir[e] == -2 && !seen[g]) dfs2(g, twin[e]);
98+
if (dir[e] == -2) { assert(free_edge == -1); free_edge = e; }
99+
else if (dir[e] == 1) ++parity;
100+
e = next[e];
101+
} while (e != component[f]);
102+
if (free_edge != -1) {
103+
dir[free_edge] = -(parity % 2 == 0 ? -1 : +1);
104+
dir[twin[free_edge]] = -dir[free_edge];
105+
}
106+
};
107+
dfs2(0, -1);
108+
return dir;
109+
}
110+
using Int = __int128_t;
111+
Int countPerfectMatching() {
112+
vector<int> dir = pfaffianOrientation();
113+
int n = vertices();
114+
vector<vector<Int>> A(n, vector<Int>(n));
115+
for (int e = 0; e < edges(); ++e)
116+
A[origin[e]][origin[twin[e]]] = dir[e];
117+
118+
// compute determinant
119+
Int det = 1;
120+
for (int j = 0; j < n; ++j) {
121+
for (int i = j+1; i < n; ++i) {
122+
while (A[i][j]) {
123+
det = -det;
124+
Int t = A[j][j] / A[i][j];
125+
for (int k = j; k < n; ++k)
126+
swap(A[i][k], A[j][k] -= t * A[i][k]);
127+
}
128+
}
129+
det *= A[j][j]; // % mod
130+
}
131+
return sqrt(det + 0.1);
132+
}
133+
};
134+
135+
using Int = long long;
136+
Int dominoCount(vector<vector<char>> table) {
137+
int m = table.size(), n = table[0].size();
138+
vector<vector<int>> index(m, vector<int>(n, -1));
139+
PlaneGraph g;
140+
unordered_map<int,unordered_map<int,int>> adj;
141+
for (int i = 0; i < m; ++i) {
142+
for (int j = 0; j < n; ++j) {
143+
if (table[i][j] == '.') {
144+
index[i][j] = g.newVertex(Point({i,j}));
145+
}
146+
}
147+
}
148+
unordered_map<int,int> next_inc, prev_inc;
149+
for (int i = 0; i < m; ++i) {
150+
for (int j = 0; j < n; ++j) {
151+
vector<int> inc;
152+
int x = index[i][j];
153+
int dx[] = {1,0,-1,0}, dy[] = {0,1,0,-1};
154+
for (int p = 0; p < 4; ++p) {
155+
int k = i+dx[p], l = j+dy[p];
156+
if (k < 0 || l < 0) continue;
157+
if (k >= table.size() || l >= table[k].size()) continue;
158+
if (table[i][j] != '.' || table[k][l] != '.') continue;
159+
int y = index[k][l];
160+
if (!adj[x].count(y)) adj[x][y] = g.newEdge(x);
161+
if (!adj[y].count(x)) adj[y][x] = g.newEdge(y);
162+
g.twin[adj[x][y]] = adj[y][x];
163+
g.twin[adj[y][x]] = adj[x][y];
164+
g.incident_edge[x] = adj[x][y];
165+
g.incident_edge[y] = adj[y][x];
166+
inc.push_back(adj[x][y]);
167+
}
168+
for (int i = 0; i < inc.size(); ++i) {
169+
int j = (i == inc.size()-1 ? 0 : i+1);
170+
next_inc[inc[i]] = inc[j];
171+
prev_inc[inc[j]] = inc[i];
172+
}
173+
}
174+
}
175+
for (int e = 0; e < g.edges(); ++e) {
176+
g.next[e] = prev_inc[g.twin[e]];
177+
g.prev[e] = g.twin[next_inc[e]];
178+
}
179+
g.completeFaces();
180+
return g.countPerfectMatching();
181+
}
182+
183+
void SPOJ_GNY07H() {
184+
int ncase;
185+
cin >> ncase;
186+
for (int icase = 0; icase < ncase; ++icase) {
187+
int w;
188+
cin >> w;
189+
vector<vector<char>> table(4, vector<char>(w, '.')) ;
190+
cout << icase+1 << " " << dominoCount(table) << endl;
191+
}
192+
}
193+
194+
int main() {
195+
SPOJ_GNY07H();
196+
}

0 commit comments

Comments
 (0)