序章-弗蘭德的祕密——利用資料性質的暴力
序章-弗蘭德的祕密
背景介紹
弗蘭德,我不知道這個地方對我意味著什麼。這裡是一切開始的地方。3年前,還是個什麼都沒見過的少年,來到弗蘭德的樹下,走進了封閉的密室,扭動的封塵已久機關,在石板上知道了這個世界最角落的最陰暗的東西。那種事情,從未忘懷,從未動搖,我還記得,那一天,我,裡修,第一次拔起了劍……
弗蘭德的密室裡,機關上方畫著兩棵樹的字樣,機關下方是一個有數字的刻度……
弗蘭德最高的兩棵樹,只要知道兩棵樹的共同的相似度就行了……
給定兩棵有根樹,可以任意刪除兩棵樹上的節點(刪除一棵節點必須保證該節點的子樹內的所有節點也必須要被刪除,換一種說法,刪除後的樹必須聯通並形成一棵樹,且根節點不能被刪除),使得刪除後的兩棵樹同構,這兩棵樹有一個共同大小,即樹的
注:兩棵同構的樹要滿足以下條件:
1、兩棵樹節點個數相等。
2、兩棵樹的以根節點的兒子為根子樹對應同構。如下圖,為兩棵同構的有根樹。
如下圖,為兩棵同構的有根樹。
一行兩個整數n,m分別表示兩棵有根樹的大小。
以下n-1行描述第一棵樹,每行兩個數x,y表示x號節點是y號節點父親。
以下m-1行描述第二棵樹,每行兩個數x,y表示x號節點是y號節點父親。
資料保證兩棵樹的1號節點均為根。
一行一個數,表示兩棵樹的相似度(刪除後最大化的同構樹的大小)。
33
12
13
12
23
2
【樣例解釋】
第一棵樹可以保留1號節點和2號節點刪除3號節點,也可以保留1號節點與3號節點刪除2號節點,
剩下的樹同構,樹的節點個數均為2。
對於30%的資料,1≤n≤10
對於60%的資料,1≤n≤100
對於100%的資料,1≤n≤1000資料保證兩棵樹上每個節點的度均不超過5。
分析:
其實這題目啊,正解和暴力都是一樣的,都是列舉兩棵樹的每個點的全排列,只是由於資料限制每個點的度不超過5,所以dp成了正解。這其實需要我們挖掘條件的能力,現在我們來挖掘一下。我們要以dp的思想去思考,一般而言dp所維護的與答案所求的有一定關係,不妨大膽設f[x][y]為A樹以x為根節點的子樹與B樹以y為根節點的子樹的最大同構數。很明顯的,我們要和暴力一樣去一一列舉配對(總感覺我在吐槽),然後搜搜搜,說的也許有點抽象,所以來串程式碼冷靜一下。
程式碼:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<queue> 6 #include<algorithm> 7 #include<vector> 8 using namespace std; 9 #define debug printf("zjyvegetable\n") 10 #define int long long 11 inline int read(){ 12 int a=0,b=1;char c=getchar(); 13 while(!isdigit(c)){if(c=='-')b=-1;c=getchar();} 14 while(isdigit(c)){a=a*10+c-'0';c=getchar();} 15 return a*b; 16 } 17 const int N=2e3+50,M=2e5+50; 18 vector<int>vec1[N],vec2[N]; 19 int n,m,f[N][N],xx,yy,vis[M]; 20 void dfss(int k,int z){ 21 if(k>=vec1[xx].size()){ 22 f[xx][yy]=max(f[xx][yy],z); 23 return; 24 } 25 dfss(k+1,z); 26 bool flag=true; 27 for(int i=0;i<vec2[yy].size();i++){ 28 if(!vis[i]){ 29 vis[i]=1; 30 flag=false; 31 dfss(k+1,z+f[vec1[xx][k]][vec2[yy][i]]); 32 vis[i]=0; 33 } 34 } 35 if(flag)f[xx][yy]=max(f[xx][yy],z); 36 } 37 void dfs(int x){ 38 if(!vec1[x].size()){ 39 for(int i=1;i<=m;i++) 40 f[x][i]=1; 41 return; 42 } 43 for(int i=0;i<vec1[x].size();i++) 44 dfs(vec1[x][i]); 45 for(int i=1;i<=m;i++){ 46 if(!vec2[i].size())f[x][i]=1; 47 else{ 48 xx=x;yy=i; 49 dfss(0,0); 50 f[x][i]++; 51 } 52 } 53 } 54 signed main(){ 55 //freopen("frand.in","r",stdin); 56 //freopen("frand.out","w",stdout); 57 int u,v; 58 n=read();m=read(); 59 for(int i=1;i<n;i++){ 60 u=read();v=read(); 61 vec1[u].push_back(v); 62 } 63 for(int i=1;i<m;i++){ 64 u=read();v=read(); 65 vec2[u].push_back(v); 66 } 67 dfs(1); 68 printf("%lld\n",f[1][1]); 69 return 0; 70 }
補充:
其實理解這段程式碼要用暴力的思想去想,你會發現其實沒什麼差。另外,這串程式碼的flag部分其實是可以省去的,這只是從意義上更好理解所以才加上的,但很容易發現flag部分所維護的最大值一定會比搜到邊界的值小(或等於)。