1. 程式人生 > 實用技巧 >NOIP2015模擬10.28B組

NOIP2015模擬10.28B組

T1: 序章-弗蘭德的祕密

洛谷AC通道!

首先熟悉一下同構的定義:

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; }