zoukankan      html  css  js  c++  java
  • 记忆化搜索 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
    ",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
    ",ans);
    120     return 0;
    121 }
  • 相关阅读:
    (剑指Offer)------二进制中1的个数
    LeetCode#58:最后一个单词的长度解析
    js 生成四个随机字母或数字+js获取当前日期
    ES6学习笔记----数组的扩展
    No component factory found for ListenerAddComponent. Did you add it to @NgModule.entryComponents?
    Can't bind to 'formGroup' since it isn't a known property of 'form'
    算法初相识---插入排序,冒泡排序,选择排序,以及分析算法
    Deno MongoDB 增删查改 接口
    Deno MySQL 增删查改接口
    Deno 几种常用的传参方式
  • 原文地址:https://www.cnblogs.com/c1299401227/p/5576903.html
Copyright © 2011-2022 走看看