日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

记忆化搜索 codevs 2241 排序二叉树

發布時間:2024/7/19 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 记忆化搜索 codevs 2241 排序二叉树 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

codevs 2241 排序二叉樹

★?? 輸入文件:bstree.in?? 輸出文件:bstree.out???簡單對比
時間限制:1 s?? 內存限制:128 MB

【問題描述】

一個邊長為n的正三角形可以被劃分成若干個小的邊長為1的正三角形,稱為單位三角形。

如右圖,邊長為3的正三角形被分成三層共9個小的正三角形,我們把它們從頂到底,從左到右以1~9編號,見右圖。同理,邊長為n的正三角形可以劃分成n2個單位三角形。

?

?四個這樣的邊長為n的正三角形可以組成一個三棱錐。我們將正三棱錐的三個側面依順時針次序(從頂向底視角)編號為A,?B,?C,底面編號為D。側面的A,?B,?C號三角形以三棱錐的頂點為頂,底面的D號三角形以它與A,B三角形的交點為頂。左圖為三棱錐展開后的平面圖,每個面上標有圓點的是該面的頂,該圖中側面A,?B,?C分別向紙內方向折疊即可還原成三棱錐。我們把這A,B、C、D四個面各自劃分成n2個單位三角形。

?

???對于任意兩個單位三角形,如有一條邊相鄰,則稱它們為相鄰的單位三角形。顯然,每個單位三角形有三個相鄰的單位三角形。現在,把1—4n2分別隨機填入四個面總共4n2個單位三角形中。

????現在要求你編程求由單位三角形組成的最大排序二叉樹。所謂最大排序二叉樹,是指在所有由單位三角形組成的排序二叉樹中節點最多的一棵樹.對于任一單位三角形,可選它三個相鄰的單位三角形中任意一個作為父節點,其余兩個分別作為左孩子和右孩子。當然,做根節點的單位三角形不需要父節點,而左孩子和右孩于對于二叉樹中的任意節點來說并不是都必須的。

【輸入】

????輸入文件為bstree.in。其中第一行是一個整數n(1<=n<=18),隨后的4n2個數,依次為三棱錐四個面上所填的數字。

【輸出】

輸出文件為bstree.out。其中僅包含一個整數,表示最大的排序二又樹所含的節點數目。

【樣例輸入】

3

19 33 32 31 29 3 5 4 30

22 24 20 21 12 24 23 34 35

14 13 15 26 18 17 8 16 27

11 10 9 1 28 7 2 6 36

【樣例】

輸入文件對應下圖:

【樣例輸出】

17

【提示】

?

輸出樣例文件對應的最大排序二叉樹如下圖所示:

?

?

1 /*正解代碼:我給加了注解,建立排序二叉樹的左右子樹的時候,一定要注意建立的上下界,否則就不是排序二叉樹*/ 2 /* 3 【算法分析】 4 在討論問題的解法之前,我們先來看看二叉排序樹的性質。 5 二叉排序樹是一棵滿足下列性質的二又樹: 6 性質1 它或是一棵空樹,或是一棵二叉樹,滿足左子樹的所有結點的值都小于根結點的值,右子樹的所有結點的值都大于根結點的值; 7 性質2 它若有子樹,則它的子樹也是二叉排序樹。 8 根據性質1,我們可以知道,二叉排序樹的左右子樹是互不交叉的。也就是說,如果確定了根結點,那么我們就可以將余下的結點分成兩個集合,其中一個集合的元素可能在左子樹上,另一集合的元素可能在右子樹上,而沒有一個結點同時可以屬于兩個集合。這一條性質,滿足了無后效性的要求,正因為二叉排序樹的左右子樹是互不交叉的,所以如果確定根結點后,求得的左子樹,對求右子樹是毫無影響的。因此,如果要使排序樹盡可能大,就必須滿足左右子樹各自都是最大的,即局部最優滿足全局最優。 9 根據性質2,二叉排序樹的左右子樹也是二叉排序樹。而前面已經分析得到,左右子樹也必須是極大的。所以,求子樹的過程也是一個求極大二叉排序樹的過程,是原問題的一個子問題。那么,求二叉排序樹的過程就可以分成若干個階段來執行,每個階段就是求一棵極大的二叉排序子樹。 10 由此,我們看到,本題中,二叉排序樹滿足階段性(性質2)和無后效性(性質1),可以用動態規劃解決。 11 下面來看具體解決問題的方法。 12 不用說,首先必須對給出的正三棱錐建圖,建成一張普通的無向圖。 13 根據正三棱錐中結點的性質,每個結點均與三個結點相連。而根據二叉排序樹的性質,當一個結點成為另一個結點的子結點后,它屬于左子樹還是右子樹也就確定下來了。所以,可以對每個結點進行狀態分離,分離出三種狀態——該結點作為與它相連的三個結點的子結點時,所得的子樹的狀態。但是,一個子結點可以根據它的父結點知道的僅僅是該子樹的一個界(左子樹為上界,右子樹為下界),還有一個界不確定,所以還需對分離出來的狀態再進行狀態分離,每個狀態代表以一個值為界(上界或下界)時的子樹狀態。 14 確定了狀態后,我們要做的事就是推出狀態轉移方程。 15 前面已經提到,一個極大的二叉排序樹,它的左右子樹也必須是極大的。因此,如果我們確定以結點n為根結點,設所有可以作為它左子結點的集合為N1,所有可以作為它右子結點的集合為N2,則以n為根結點、結點大小在區間[l, r]上的最大二叉排序樹的結點個數為: 16 【動態規劃】排序二叉樹 17 我們所要求的最大的二叉排序樹的結點個數為:【動態規劃】排序二叉樹 18 從轉移方程來看,我們定義的狀態是三維的。那么,時間復雜度理應為O(n3)。其實并非如此。每個結點的狀態雖然包含下界和上界,但是不論是左子結點還是右子結點,它的一個界取決于它的父結點,也就是一個界可用它所屬的父結點來表示,真正需要分離的只有一維狀態,要計算的也只有一維。因此,本題時間復雜度是O(n2)(更準確的說應該是O(3n2))。 19 此外,由于本題呈現一個無向圖結構,如果用遞推形式來實現動態規劃,不免帶來很大的麻煩。因為無向圖的階段性是很不明顯的,盡管我們從樹結構中分出了階段。不過,實現動態規劃的方式不僅僅是遞推,還可以使用搜索形式——記憶化搜索。用記憶化搜索來實現本題的動態規劃可以大大降低編程復雜度。 20 */ 21 /*----------------代碼----------------*/ 22 #include<iostream> 23 #include<cstdio> 24 using namespace std; 25 const int Limitn=18+2; 26 const int Limitpoint=1296+4; 27 int n; 28 int s[5][20][40]; 29 int c[Limitpoint][4]; 30 bool vis[Limitpoint][Limitpoint]; 31 int f[Limitpoint][4][Limitpoint]; 32 int best; 33 void init() 34 { 35 scanf("%d",&n);/*s[k][i][j],第k個三角形的第i行的第j個*/ 36 for (int k=1;k<=4;k++) 37 for (int i=1;i<=n;i++) 38 for (int j=1;j<=i*2-1;j++) 39 scanf("%d",&s[k][i][j]); 40 } 41 void link(int a,int b) 42 { 43 if (!vis[a][b]) 44 { 45 vis[a][b]=1; 46 c[a][++c[a][0]]=b;/*鄰接表建邊,a不是節點的編號,而是一個數*/ 47 } 48 if (!vis[b][a]) 49 { 50 vis[b][a]=1; 51 c[b][++c[b][0]]=a; 52 } 53 } 54 void make_graph() 55 { 56 for (int k=1;k<=4;k++) 57 for (int i=2;i<n;i++) 58 for (int j=2;j<i*2-1;j++) 59 {/*三角形內部建邊*/ 60 link(s[k][i][j],s[k][i][j-1]); 61 link(s[k][i][j],s[k][i][j+1]); 62 if (j%2) 63 link(s[k][i][j],s[k][i+1][j+1]); 64 else 65 link(s[k][i][j],s[k][i-1][j-1]); 66 } 67 for (int k=1;k<=4;k++) 68 for (int j=2;j<=n*2-1;j+=2) 69 {/*三角形最后一層建邊*/ 70 link(s[k][n][j],s[k][n][j-1]); 71 link(s[k][n][j],s[k][n][j+1]); 72 link(s[k][n][j],s[k][n-1][j-1]); 73 } 74 for (int k=1,i=1;k<=n;k++,i++) 75 {/*相鄰的棱三角形建邊*/ 76 link(s[1][i][1],s[3][i][i*2-1]); 77 link(s[1][i][i*2-1],s[2][i][1]); 78 link(s[2][i][i*2-1],s[3][i][1]); 79 } 80 for (int j=1;j<=n*2-1;j+=2) 81 {/*其他三角形與第四個三角形建邊*/ 82 link(s[1][n][j],s[4][n-(j/2)][1]); 83 link(s[2][n][j],s[4][j/2+1][((j/2)+1)*2-1]); 84 link(s[3][n][j],s[4][n][n*2-j]); 85 } 86 } 87 int tree_max(int i,int limit1,int limit2) 88 { 89 int from=1;/*找出i的父親。防止又走回去*/ 90 while (c[i][from]!=limit2) from++; 91 if (f[i][from][limit1]>0) return f[i][from][limit1];/*記憶化搜索*/ 92 int l,r; 93 if (limit1>limit2)/*建立右子樹的邊界*/ 94 { 95 l=limit2+1; 96 r=limit1; 97 } 98 else/*建立左子樹的邊界*/ 99 { 100 l=limit1; 101 r=limit2-1; 102 } 103 int lmax=0,rmax=0; 104 for (int j=1;j<=3;j++)/*枚舉當前點的所有鄰接點,不找到父親,而且符合上下邊界*/ 105 if (j!=from && (l<=c[i][j] && c[i][j]<=r)) 106 if (c[i][j]<i)/*建立左子樹*/ 107 lmax=max(lmax,tree_max(c[i][j],l,i)); 108 else/*建立右子樹*/ 109 rmax=max(rmax,tree_max(c[i][j],r,i)); 110 f[i][from][limit1]=lmax+rmax+1; 111 return f[i][from][limit1]; 112 } 113 void dfs() 114 { 115 best=0; 116 for (int i=1;i<=n*n*4;i++)/*枚舉所有點*/ 117 { 118 int lmax=0,rmax=0; 119 for (int j=1;j<=3;j++) 120 if (c[i][j]<i)/*c[i][j]比i小,就建立左子樹*/ 121 lmax=max(lmax,tree_max(c[i][j],1,i)); 122 else 123 rmax=max(rmax,tree_max(c[i][j],n*n*4,i)); 124 best=max(best,lmax+rmax+1);/*別忘了加上它本身的根節點*/ 125 } 126 } 127 int main() 128 { 129 130 init(); 131 make_graph(); 132 dfs(); 133 printf("%d\n",best); 134 return 0; 135 }

?我的代碼:

1 #include<iostream> 2 using namespace std; 3 #include<cstdio> 4 #include<cstdlib> 5 #define Njd 2000 6 #include<cstring> 7 #define K 5 8 #define L 20 9 int gra[K][L][L<<1]; 10 int edge[Njd][5]; 11 int f[Njd][K][Njd]={0}; 12 int n,ans=0; 13 bool vis[Njd][Njd]={0}; 14 void add_edge(int a,int b) 15 { 16 if(!vis[a][b]) 17 { 18 vis[a][b]=true; 19 edge[a][++edge[a][0]]=b; 20 } 21 if(!vis[b][a]) 22 { 23 vis[b][a]=true; 24 edge[b][++edge[b][0]]=a; 25 } 26 } 27 void input() 28 { 29 scanf("%d",&n); 30 for(int i=1;i<=4;++i) 31 for(int j=1;j<=n;++j) 32 for(int k=1;k<=(2*j-1);++k) 33 scanf("%d",&gra[i][j][k]); 34 } 35 void build_graph() 36 { 37 for(int i=1;i<=4;++i) 38 for(int j=2;j<n;++j) 39 for(int k=2;k<=(2*j-2);++k) 40 { 41 add_edge(gra[i][j][k],gra[i][j][k-1]); 42 add_edge(gra[i][j][k],gra[i][j][k+1]); 43 if(k%2==0) 44 { 45 add_edge(gra[i][j][k],gra[i][j-1][k-1]); 46 } 47 else add_edge(gra[i][j][k],gra[i][j+1][k+1]); 48 49 } 50 for(int k=1;k<=4;++k) 51 for(int j=2;j<=(2*n-1);j+=2) 52 { 53 add_edge(gra[k][n][j],gra[k][n][j-1]); 54 add_edge(gra[k][n][j],gra[k][n][j+1]); 55 add_edge(gra[k][n][j],gra[k][n-1][j-1]); 56 } 57 for(int i=1;i<=n;++i) 58 { 59 add_edge(gra[1][i][2*i-1],gra[2][i][1]); 60 add_edge(gra[1][i][1],gra[3][i][2*i-1]); 61 add_edge(gra[2][i][2*i-1],gra[3][i][1]); 62 } 63 for(int i=1;i<=n;++i) 64 { 65 add_edge(gra[4][i][2*i-1],gra[2][n][2*i-1]); 66 add_edge(gra[4][i][1],gra[1][n][2*n-(2*i-1)]); 67 add_edge(gra[4][n][2*i-1],gra[3][n][2*n-(2*i-1)]); 68 } 69 } 70 int memory_dfs(int k,int limit1,int limit2) 71 { 72 int from=1; 73 while(edge[k][from]!=limit2) 74 { 75 from++; 76 } 77 if(f[k][from][limit1]>0) 78 return f[k][from][limit1]; 79 int l,r; 80 if(limit1>limit2) 81 { 82 r=limit1;l=limit2+1; 83 } 84 else { 85 l=limit1;r=limit2-1; 86 } 87 int lmax=0,rmax=0; 88 for(int j=1;j<=3;++j) 89 { 90 if(j!=from&&edge[k][j]>=l&&edge[k][j]<=r) 91 { 92 if(edge[k][j]<k) 93 lmax=max(lmax,memory_dfs(edge[k][j],l,k)); 94 else rmax=max(rmax,memory_dfs(edge[k][j],r,k)); 95 96 } 97 98 } 99 f[k][from][limit1]=lmax+rmax+1; 100 return f[k][from][limit1]; 101 } 102 int main() 103 { 104 input(); 105 build_graph(); 106 ans=0; 107 int lmax=0,rmax=0; 108 for(int i=1;i<=n*n*4;++i) 109 { 110 lmax=0;rmax=0; 111 for(int j=1;j<=3;++j) 112 { 113 if(edge[i][j]>0&&edge[i][j]<i) 114 lmax=max(lmax,memory_dfs(edge[i][j],1,i)); 115 else rmax=max(rmax,memory_dfs(edge[i][j],n*n*4,i)); 116 } 117 ans=max(ans,lmax+rmax+1); 118 } 119 printf("%d\n",ans); 120 return 0; 121 }

?

轉載于:https://www.cnblogs.com/c1299401227/p/5576903.html

總結

以上是生活随笔為你收集整理的记忆化搜索 codevs 2241 排序二叉树的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。