NOIP2015模擬10.28B組
阿新 • • 發佈:2020-07-24
T1: 序章-弗蘭德的祕密
首先熟悉一下同構的定義:
1、兩棵樹節點個數相等。
2、兩棵樹的以根節點的兒子為根子樹對應同構。如下圖,為兩 棵同構的有根樹。
看複雜度,我們一般會想到用$O(N^2)$的演算法吧。
那麼,考慮樹形DP。
設 $f_{i,j}$表示兩棵樹分別以$i, j$為根時的最大同構值。
那麼,我們要想得到$f_{i,j}$,肯定要先得知其兒子。那麼,如上圖,對於$x,y$來講,$f_{x,y} = max( f_{sonx, sony} ) + 1$
於是,我們對於樹$1$每個點,對應列舉樹$2$每個點,同時列舉雙方兒子進行轉移。
總複雜度: $O(5!N^2)$
#include <bits/stdc++.h> using namespace std; #define N 1010 inline int read(){ int x = 0 ,s = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }return x * s; } vector <int> G[N]; vector <int> T[N]; int f[N][N], x, y; // f[x][y]: x、y作為根節點的最大同構數 int vis[N]; int n, m; void dfs1(int best, int step){ // 上一層最優解 + 已搜兒子個數 if(step > G[x].size()){ f[x][y] = max(f[x][y], best); return ; } dfs1(best, step + 1); bool judge = 0; for(int i = 0; i < T[y].size(); i++){ if(!vis[i]){ vis[i] = 1; judge = 1; dfs1(best + f[G[x][step - 1]][T[y][i]], step + 1); vis[i] = 0; } } if(!judge) f[x][y] = max(f[x][y], best); // 遞迴下去有最優解時不用更新 return ; } void dfs(int now){ if(G[now].size() == 0){ // 第一棵樹無兒子, 只有自己 for(int i = 1;i <= m; i++) f[now][i] = 1; return ; } for(int i = 0; i < G[now].size(); i++) dfs(G[now][i]); for(int i = 1; i <= m; i++){ if(T[i].size() == 0) f[now][i] = 1; // 檢查第二棵樹是否有兒子 else { x = now, y = i; dfs1(0, 1); f[now][i]++; } } return ; } int main(){ // freopen("frand.in", "r", stdin); // freopen("frand.out", "w", stdout); n = read(), m = read(); for(int i = 1;i < n; i++){ int x = read(), y = read(); G[x].push_back(y); } for(int i = 1; i < m; i++){ int x = read(), y = read(); T[x].push_back(y); } dfs(1); printf("%d\n", f[1][1]); return 0; }