J Identical Trees
https://www.cnblogs.com/lesning/p/13476608.html
神奇的树形DP+二分图最大权值匹配转移
dp[x][y]表示以左边的树x为根,右边的树y为根,他们有dp[x][y]个序号是重合的,若x和y不同构那就dp[x][y] = -INF;
第一步利用get函数获得每一种树的形式编号,cnt1[x] = cnt2[y]就代表x和y同构,
第二步就跑费用流,每次跑都重新建图(删除之前建过的图)
如何转移?
给x的儿子们和y的儿子们建个二分图跑最大权值匹配,跑下来的最大权值就是儿子们的答案。比赛时候感觉复杂度有点玄学不敢写罢了,
本人代码弱智,易于理解,详细的看代码把,
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 3020;
int n, m;
queue<int> que;
int head[maxn];
unordered_map<string, int>id;
struct Edge {
int id, nex;
int w, f;
}edge[40400];
int cnt = 0;
void init() {
cnt = 0;
memset(head, -1, sizeof(head));
while (que.size()) que.pop();
}
void add_(int x, int y, int f, int w) {
edge[cnt].id = y;
edge[cnt].f = f;
edge[cnt].w = w;
edge[cnt].nex = head[x];
head[x] = cnt++;
}
void add(int x, int y, int f, int w) {
add_(x, y, f, w);
add_(y, x, 0, -w);
}
int vis[maxn];
int dis[maxn];
int mflow[maxn];
int per[maxn];
int spfa(int s, int t) {
memset(vis, 0, sizeof(vis));
memset(dis, inf, sizeof(dis));
mflow[s] = inf;
que.push(s);
dis[s] = 0;
vis[s] = 1;
while (que.size()) {
int x = que.front();
que.pop();
vis[x] = 0;
for (int i = head[x]; ~i; i = edge[i].nex) {
int v = edge[i].id;
if (dis[v] > dis[x] + edge[i].w && edge[i].f) {
dis[v] = dis[x] + edge[i].w;
per[v] = i;
mflow[v] = min(mflow[x], edge[i].f);
if (vis[v]) continue;
vis[v] = 1;
que.push(v);
}
}
}
if (dis[t] != inf) return 1;
else return 0;
}
void update(int s, int t, int& flow) {
int minn = mflow[t];
for (int i = t; i != s; i = edge[per[i] ^ 1].id) {
int x = per[i];
edge[x].f -= minn;
edge[x ^ 1].f += minn;
}
flow += minn;
}
int solve(int s, int t, int& flow) {
int ans = 0;
while (spfa(s, t)) {//用spfa开路
ans += dis[t] * mflow[t];
update(s, t, flow);
}
return ans;
}
ll dp[550][550];
vector<int>G1[maxn], G2[maxn];
int jude[550][550];
int aa = 0;
int cnt1[550], cnt2[550];
int dfs(int x, int y) {
//看看x和y是否同构
if (jude[x][y] == 0) {
dp[x][y] = -inf;
return 0;
}
if (x == y) {
dp[x][y] = 1;
if (G1[x].size() == 0) return 0;
}
for (int i = 0; i < G1[x].size(); i++) {
for (int j = 0; j < G2[y].size(); j++) {
int a = G1[x][i];
int b = G2[y][j];
dfs(a, b);
}
}
init();
for (int i = 0; i < G1[x].size(); i++) add(2010, i, 1, 0);
for (int j = 0; j < G2[y].size(); j++) add(j + 505, 2011, 1, 0);
for (int i = 0; i < G1[x].size(); i++) {
for (int j = 0; j < G2[y].size(); j++) {
int a = G1[x][i];
int b = G2[y][j];
add(i, j + 505, 1, -dp[a][b]);
}
}
int cns = 0;
int ans = solve(2010, 2011, cns);
dp[x][y] -= ans;
//cout << x << "----------" << y << ":\t" << dp[x][y] <<"\t"<< cns << endl;
return 0;
}
int get(int x, int y) {
for (int i = 0; i < G1[x].size(); i++) {
for (int j = 0; j < G2[y].size(); j++) {
int a = G1[x][i];
int b = G2[y][j];
get(a, b);
}
}
string sn;
if (G1[x].size() == G2[y].size()) {
if (G1[x].size() == 0) {
jude[x][y] = 1;
}
else {
for (int p : G1[x]) {
sn += (char)(cnt1[p] + '0');
}
sort(sn.begin(), sn.end());
if (id[sn] == 0) id[sn] = ++aa;
cnt1[x] = id[sn];
sn.clear();
for (int p : G2[y]) {
sn += (char)(cnt2[p] + '0');
}
sort(sn.begin(), sn.end());
if (id[sn] == 0) id[sn] = ++aa;
cnt2[y] = id[sn];
sn.clear();
if (cnt1[x] == cnt2[y]) jude[x][y] = 1;
}
}
return 0;
}
int main() {
scanf("%d", &n);
int root1, root2;
int x;
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
if (x == 0) root1 = i;
else G1[x].push_back(i);
}
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
if (x == 0) root2 = i;
else G2[x].push_back(i);
}
for (int i = 1; i <= n; i++) dp[i][i] = 1;
get(root1, root2);
dfs(root1, root2);
cout << n - dp[root1][root2] << endl;
}
/*
7
0 1 1 2 2 3 3
0 1 1 3 3 2 2
输出2
*/
京公网安备 11010502036488号