测试总结

今后将有大量试题与博客出没(想回去中考。。。)

T1:

FBI树

我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。

FBI树是一种二叉树,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2^N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:

1) T的根结点为R,其类型与串S的类型相同;

2) 若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1S2;由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2

现在给定一个长度为2^N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历序列。

看上去很麻烦,实际上就是根据原字符串进行处理,每次将字符串从中间分为两等长字符串,再分别判断类型

结合代码讲下:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<queue>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<string>
  7 using namespace std;
  8 int n;
  9 struct node{     //其中数组大小很鬼畜,一开始大小为1024左右,然而RE,就开了十倍大小,luogu过了
 10     int size;
 11     char a[10300];
 12     char type;
 13 }nd[10240];
 14 int po(int k){      //快速幂,其实用pow好像慢不了多少,因为本函数在本题中并不常用
 15     int t=2;
 16     int p=1;
 17     while(k){
 18         if(k&1)
 19             p*=t;
 20         t*=t;
 21         k>>=1;
 22     }
 23     return p;
 24 }
 25 inline void search(int numb,int de){      //因为用的特别多,加了inline,递归处理字符串
 26     int l=numb*2;         //根据满二叉树的节点编号特征将节点编号表示出来
 27     int r=numb*2+1;
 28     nd[l].size=nd[numb].size/2;
 29     nd[r].size=nd[numb].size/2;
 30     for(int i=0;i<nd[numb*2].size;i++){
 31         nd[l].a[i]=nd[numb].a[i];
 32         nd[r].a[i]=nd[numb].a[i+nd[l].size];
 33     }  //一人一半
 34     bool ok1=0,ok0=0;
 35     for(int i=0;i<nd[l].size;i++){  //遍历判断字符串类型
 36         if(nd[l].a[i]=='1')
 37             ok1=1;
 38         if(nd[l].a[i]=='0')
 39             ok0=1;
 40         if(ok1&&ok0)
 41             nd[l].type='F';
 42         else if(!ok1)
 43             nd[l].type='B';
 44         else if(!ok0)
 45             nd[l].type='I';
 46     }
 47     ok1=0;ok0=0;
 48     for(int i=0;i<nd[r].size;i++){
 49         if(nd[r].a[i]=='1')
 50             ok1=1;
 51         if(nd[r].a[i]=='0')
 52             ok0=1;
 53         if(ok1&&ok0)
 54             nd[r].type='F';
 55         else if(!ok1)
 56             nd[r].type='B';
 57         else if(!ok0)
 58             nd[r].type='I';
 59     }
 60     if(de==n+1) return;  //处理完毕的边界条件
 61     else{                //递归处理
 62         search(l,de+1);
 63         search(r,de+1);
 64     }
 65 }
 66 //<-编号鬼畜了 
67
void print(int numb){ //后序输出,也是递归 68 if(numb>po(n+1)-1) return; 69 print(numb*2); 70 print(numb*2+1); 71 putchar(nd[numb].type); 72 } 73 int main(){ 74 //freopen("fbi.in","r",stdin); 75 //freopen("fbi.out","w",stdout); 76 scanf("%d",&n); 77 scanf("%s",nd[1].a); 78 nd[1].size=po(n); 79 bool ok1,ok0; 80 for(int i=0;i<nd[1].size;i++){ //判断原字符串 81 if(nd[1].a[i]=='1') 82 ok1=1; 83 if(nd[1].a[i]=='0') 84 ok0=1; 85 if(ok1&&ok0) 86 nd[1].type='F'; 87 else if(!ok1) 88 nd[1].type='B'; 89 else if(!ok0) 90 nd[1].type='I'; 91 } 92 search(1,1);/* 93 for(int i=1;i<=15;i++){ 94 cout<<nd[i].size<<endl; 95 for(int j=0;j<nd[i].size;j++) 96 cout<<nd[i].a[j]; 97 cout<<endl; 98 cout<<nd[i].type<<endl; 99 cout<<endl; 100 } 调试用的*/ 101 print(1); 102 return 0; 103 }

这回显示行号以示友好...

本题很简单,然而本机测试无伤,手动测试无伤,lemon测试不过???丢了10分,导致掉了3名

那个数据还是不属于数据范围的...

T2:

医院设置

设有一棵二叉树,如图:

其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为1。如上图中,

若医院建在1 处,则距离和=4+12+2*20+2*40=136;若医院建在3 处,则距离和=4*2+13+20+40=81……

就是求路程最小情况的总路程

乍一看:又是树?LCA?

看看luogu标签(当然我是刚刚(考试后)看的):

到底是啥?

一琢磨就知道这好像是最短路问题,看看可爱的数据范围:

第一行一个整数n,表示树的结点数。(n≤100)

n3好像没问题

就是用Floyd

代码(5分钟的产品...):

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
int n;
int peo[105];
int dis[105][105];
int main(){
    //freopen("hospital.in","r",stdin);
    //freopen("hospital.out","w",stdout);
    memset(dis,0x3f,sizeof(dis));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        dis[i][i]=0;
    for(int i=1;i<=n;i++){
        int a,b;
        scanf("%d%d%d",&peo[i],&a,&b);
        if(a!=0){
            dis[i][a]=1;
            dis[a][i]=1;
        }
        if(b!=0){
            dis[i][b]=1;
            dis[b][i]=1;
        }
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(dis[i][j]>dis[i][k]+dis[k][j]){
                    dis[i][j]=dis[i][k]+dis[k][j];
                    dis[j][i]=dis[i][k]+dis[k][j];
                }
    int minn=2147483647;
    for(int i=1;i<=n;i++){
        int sum=0;
        for(int j=1;j<=n;j++){
            sum+=peo[j]*dis[i][j];
        }
        minn=min(minn,sum);
    }
    cout<<minn;
    return 0;
}

Floyd模板加枚举暴力,不解释...

T3:

加分二叉树

设一个nn个节点的二叉树tree的中序遍历为(1,2,3,…,n1,2,3,,n),其中数字1,2,3,…,n1,2,3,,n为节点编号。每个节点都有一个分数(均为正整数),记第ii个节点的分数为di,treedi,tree及它的每个子树都有一个加分,任一棵子树subtreesubtree(也包含treetree本身)的加分计算方法如下:

subtreesubtree的左子树的加分× subtreesubtree的右子树的加分+subtreesubtree的根的分数。

若某个子树为空,规定其加分为11,叶子的加分就是叶节点本身的分数。不考虑它的空子树。

试求一棵符合中序遍历为(1,2,3,…,n1,2,3,,n)且加分最高的二叉树treetree。要求输出;

(1)treetree的最高加分

(2)treetree的前序遍历

这个题看似难,

实际确实很难

最常见的解法就是区间dp和记忆化搜索(都不会

代码:

#include<iostream>
#include<cstdio>
using namespace std;
int n;
long long dp[35][35];         //注意,和有可能大于231,用dp_i_j表示从i——j最大情况
int rt[35][35];
int a[35];
void print(int l, int r) {     //同T1递归处理
    if(l>r)return;
    if(l==r){
        printf("%d ",l);
        return;
    }
    printf("%d ",rt[l][r]);
    print(l,rt[l][r]-1);
    print(rt[l][r]+1,r);
}
int main(){
    //freopen("binary.in","r",stdin);
    //freopen("binary.out","w",stdout);
  scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        dp[i][i]=a[i];
        dp[i][i-1]=1;                //处理这里的目的是接下来有可能k=i,此时无右子树,所以需将其处理为1
        rt[i][i]=i;
    }
    for(int len=2;len<=n;len++){     //枚举区间,即需处理范围
        for(int i=1;i<=n-len+1;i++){
            int j=i+len-1;
            for(int k=i;k<=j;k++){   //枚举根结点
                if(!dp[k+1][j]) dp[k+1][j]=1;    //将空区间设为1
                if(!dp[i][k])dp[i][k]=1;
                if(dp[i][k-1]*dp[k+1][j]+a[k]>dp[i][j]){
                    dp[i][j]=dp[i][k-1]*dp[k+1][j]+a[k];   //状态转移,找到最优方案将其保存
                    rt[i][j]=k;                            //此处不用max()函数因为还要判断保存根结点
                }
            }
        }
    }
    cout<<dp[1][n]<<endl;
    print(1,n);
    return 0;
}

 由题意可知,要求的最大加分即为左右两子树乘积加根结点,所以dp方程如下:

  dp[i][j]=max(dp[i][k-1]*dp[k+1][j]+a[k])

同时进行根的保存,用于输出前序遍历,此处rt[i][j]指的从i——j加分最大子树的根结点,方便递归处理,

最后输出全局结果,就是把整个树的情况输出,dp[1][n]显然指从1——n结点成树最大情况,而print 1——n指的是将刚刚1——n结点成树的图输出

posted @ 2019-06-11 07:10  _Alex_Mercer  阅读(212)  评论(0编辑  收藏  举报