先枚举割掉一个边,使得树分成两个树
然后枚举两个树的根,来比较这两个树的最大同构子树大小。
需要加个记忆化:dp[i][ifather][j][jfather],即以ifather为父亲的i节点为根的子树和jfather为父亲j节点为根的子树的最大同构子树的大小
看上去是个4维的,但其实[i][ifather]这两维合法的只有O(n),即边的个数。同理,这个数组合法的其实只有n^2级别个。
比如对于i和j两个节点,处理以他们为根的最大同构树,先枚举他们的孩子分支,即得到一个n*m的矩阵是他们孩子分支最大同构的大小。。
这其实是一个二分图带权最优匹配。
#include <vector> #include <list> #include <map> #include <set> #include <deque> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iostream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <ctime> #include <cstring> #include <queue> using namespace std; class DeerInZooDivOne { public: int getmax(vector<int> , vector<int> ); }; vector<int> nt[55]; int dp[55][55][55][55]; /*************************************************/ #define N 200 #define inf 2000000000 #define Min(a,b) (((a)<(b))?a:b) struct edge { int s, t, f, v, next; } e[300 * N]; int eid; int node[N]; void init() { memset(node, -1, sizeof(node)); eid = 0; } void addedge(int s, int t, int f, int v) { e[eid].s = s; e[eid].t = t; e[eid].f = f; e[eid].v = v; e[eid].next = node[s]; node[s] = eid++; e[eid].s = t; e[eid].t = s; e[eid].f = 0; e[eid].v = -v; e[eid].next = node[t]; node[t] = eid++; } int dist[N], path[N]; bool in[N]; queue<int> q; int SPFA(int s, int t, int n) { int i, u, v; memset(in, false, sizeof(in)); for (i = 0; i < n; ++i) dist[i] = inf; dist[s] = 0; path[s] = -1; q.push(s); in[s] = true; while (!q.empty()) { u = q.front(); q.pop(); in[u] = false; for (i = node[u]; i != -1; i = e[i].next) { v = e[i].t; if (e[i].f > 0 && dist[u] + e[i].v < dist[v]) { dist[v] = dist[u] + e[i].v; path[v] = i; if (!in[v]) { q.push(v); in[v] = true; } } } } return dist[t] != inf; } void output() { int i; for (i = 0; i < eid; ++i) printf("s:%d t:%d f:%d v:%d next:%d\n", e[i].s, e[i].t, e[i].f, e[i].v, e[i].next); } int max_flow_min_cost(int s, int t, int n) { int i, minf, f = 0, cost = 0; // output(); while (SPFA(s, t, n)) { for (i = path[t], minf = e[path[t]].f; i != -1; i = path[e[i].s]) minf = Min(minf,e[i].f); f += minf; for (i = path[t]; i != -1; i = path[e[i].s]) { cost += minf * e[i].v; e[i].f -= minf; e[i ^ 1].f += minf; } } return cost; } /*************************************************/ int gao2(int n, int m, int graph[][55]) { int i, j; init(); int s = n + m; int t = n + m + 1; for (i = 0; i < n; ++i) { addedge(s, i, 1, 0); addedge(i, t, 1, 0); } for (i = 0; i < m; ++i) { addedge(i + n, t, 1, 0); } for (i = 0; i < n; ++i) { for (j = 0; j < m; ++j) { addedge(i, j + n, 1, -graph[i][j]); } } return -max_flow_min_cost(s, t, t + 1); } int gao(int i, int fi, int j, int fj) { int ki, kj; int ret = 1; if (dp[i][fi][j][fj] != -1) return dp[i][fi][j][fj]; int graph[55][55]; memset(graph, 0, sizeof(graph)); for (ki = 0; ki < nt[i].size(); ++ki) { if (nt[i][ki] == fi) continue; for (kj = 0; kj < nt[j].size(); ++kj) { if (nt[j][kj] == fj) continue; graph[ki][kj] = gao(nt[i][ki], i, nt[j][kj], j); } } ret += gao2(nt[i].size(), nt[j].size(), graph); return dp[i][fi][j][fj] = dp[j][fj][i][fi] = ret; } bool dfs(int i, int fa, int j) { if (i == j) return true; int k; for (k = 0; k < nt[i].size(); ++k) { if (nt[i][k] == fa) continue; if (dfs(nt[i][k], i, j)) return true; } return false; } int DeerInZooDivOne::getmax(vector<int> A, vector<int> B) { int i, j, k, ii, jj, n; int ans = 0; n = A.size() + 1; for (k = 0; k < A.size(); ++k) { for (i = 0; i < n; ++i) nt[i].clear(); for (i = 0; i < A.size(); ++i) { if (i == k) continue; nt[A[i]].push_back(B[i]); nt[B[i]].push_back(A[i]); } for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { dp[i][n][j][n] = -1; for (ii = 0; ii < nt[i].size(); ++ii) { for (jj = 0; jj < nt[j].size(); ++jj) dp[i][nt[i][ii]][j][nt[j][jj]] = -1; } } } for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { if (dfs(i, -1, j) == true) continue; ans = max(ans, gao(i, n, j, n)); } } } return ans; }