上海交通大学2005年计算机复试机试题详解

第一部分:考试说明

           ◎考试说明◎

题目与时间
本次上机考试分两部分,第一部分为试机,半小时,只有一道题,供大家熟悉上机环境
,不计成绩。第二部分为正式考试,三个小时,共三道题。

评分方法:
我们采取黑盒测试法评分。对您的每个程序,我们将使用若干个测试数据进行测试,您
的程序在时间限制内每通过一个测试数据就能得到相应的分数。第一题有8个数据,第二
题有6个数据,第三题有6个数据,共20个数据,每个数据5分,总分100分。

程序提交说明:
  请在D盘中建立一个您的考生号(?)为目录名的文件夹,并务必将您的程序存放在这
个文件夹中,文件名必须取为:大写题号.cpp(.c/.java),如A.cpp,B.c或C.java等,
任何其他文件名的程序不予评分。请勿对同一道题提交不同语言版本的程序,否则对该
题我们将不予评分。


其它:
l 请在您的程序中一律采用标准输入输出(屏幕),具体可参照我们给出的Sample程

2 对每个测试数据,您的程序的运行时间限制为1秒
3 如果您需要使用Eclipse,请运行E:/exam/eclipse/run.bat


对您的建议:
l 仔细阅读题目,一定要确保您的输入输出符合要求
2 仔细阅读和理解Sample Input and Output能帮助您正确理解题意,甚至想出解题方

3 不必书写注释,更不必美化程序,我们只进行黑盒测试
4 您可以参考题目中所给提示进行编程,但也完全可以不参考,只要您的程序能够完
成给定要求
5 如果您没有把握完成一个能正确处理所有输入数据情况的程序,您可以考虑在程序
中仅处理一些数据规模不是太大的情况,这样仍可能使您得到大多的分数
6 不要编写破坏性程序,否则产生的结果对您也是破坏性的


第二部分:原题
Problem A: Goldbach's Conjecture

The famous Goldbach's conjecture states that a sufficiently large positive
even number can always be decomposed as the sum of a pair of prime numbers.
This conjecture has never been proved or disproved, which left as a great
challenge to human beings. Take it easy, we don't require you to solve the
conjecture here. Instead, you are only required to write a verifier tool
for the conjecture, which decomposes a positive integer(NOT necessarily
large even ones) into a pair of prime numbers if possible.

Input Specification:
The positive integer N which is no greater than 100,000.

Output Specification:
In a single line the two prime numbers p1 and p2 separated by a single
space, or a single 0 if no such pair exist. p1 must be no greater than p2
and if multiple pairs of p1 and p2 exist as solutions, output the pair of
p1, p2 such that p1 is the smallest.

Hint:
l Take care of the cases where N is very small and the cases where N is
odd
2 Be sure that you output the correct pair if multiple pairs exist

Sample Input 1:
19
Sample Output 1:
2 17

Sample Input 2:
24
Sample Output 2:
5 19

 

Problem B: Mines

Many mines have been detected to exist in a small area of the Sahara desert,
which is a severe threat to the habitants nearby. Those mines are quite
special and called the "Super Chaining" ones because if any one of them is
triggered and exploded, every mine within 5 meters from it would also be
triggered. Those triggered mines explode and continue to trigger other
mines in the same manner, a kind of chain reaction. So terrible it is!
With the help of a newly developed mine detector, Lieutenant John has
recently been able to detect the exact positions of all the mines. And now,
he is making a plan on how to eliminate all the mines by triggering all of
them, an economic way as it is in the desert. As a young sergeant, your
mission now is to help Lieutenant to evaluate the effect of triggering one
single mine which Lieutenant chooses to be the first to trigger. That is,
for a given mine, you must decide which mines will also be triggered as
side effect.

Input Specification:
In the first line, an integer N(2<=N<=100) represents the number of mines.
In the ith(i=1..N) of the following N lines, two integers Xi and
Yi(-1000<=Xi,Yi<=1000) in meters represent the cartesian position of the
ith mine. The final line includes one integer representing which mine to
trigger first.

Output Specification:
In a single line, output the list of order numbers of mines that will also
be triggered in the chain reaction (including the firstly triggered one).
The order numbers must be in ascending order and separated by single spaces.

Hint:
l You might consider either a depth-first search or a breadth first
search of mines to solve the problem

Sample Input:
6
0 0
2 2
4 2
10 10
-1 6
-1 7
2

Sample Output:
1 2 3 5 6
Note:
In the above sample:
(2, 2) triggers (0, 0); (2, 2) triggers (4, 2); (2, 2) triggers (-1, 6) and
(-1, 6) then triggers (-1, 7); (10, 10) cannot be triggered.

Problem C: Mr. Bean

Mr. Bean likes bean games, especially the "beanary tree" game. To enjoy a
beanary tree game, he plays the following steps:
l He firstly picks one bean from each of his N different kinds of beans
and randomly arranges them into a sequence, that is, a sequence of N
different beans. Mr. Bean calls this sequence the preorder sequence.
l Then, he picks another random bean sequence in the same way, called the
inorder sequence.(strange names, aren't they?)
l Now come to the difficult step. Mr. Bean picks yet another N different
beans and, this time, he tries to put them into…guess what, no no, not a
sequence, but a tree! A beanary-tree!
What is a beanary-tree? A beanary-tree is in fact a special kind of binary
tree of beans. Such tree has the magic property that if you picks its beans
in pre-order(recursively parent then left then right), the bean sequence
thus obtained is just the same as the preorder sequence he randomly picked
in the first step! Besides, if you pick the beans in in-order(recursively
left then parent then right), the bean sequence thus obtained is just the
same as the inorder sequence! What a magic.
l This is not the end of the game. Mr. Bean finally picks the beans in
postorder(recursively left then right then parent) and record the resulted
postorder sequence in his notebook.(sequences save space, unlike trees…)


This figure illustrates the preorder, inorder, postorder of a binary tree

The game is difficult. Sometimes it's even impossible to finish the
process. Usually, the old Bean found that he is too old for doing this, so
he asks you for help. Please write a clever program that could input the
preorder sequence and the inorder sequence and then play the rest of the
game. To make your life easier, you only need to output the corresponding
postorder sequence if possible.

Input Specification:
The input only contains two strings. The first string represents the
preorder sequence and the second string represents the inorder sequence.
Each of the strings consists only of capital letters. Different capital
letters represent different kinds of beans. You may assume that the input
is always meaningful for the game.

Output Specification:
In a single line, output a string representing the postorder sequence. If
it is impossible, output the string "-_-bbb"(no quotation mark should be
printed).

Sample Input 1:
KGCBDHQMPY
BCDGHKMPQY

Sample Output 1:
BDCHGPMYQK

Sample Input 2:
ABC
CAB

Sample Output 2:
-_-bbb

详细解答,全部用C++编写,复制后保存为CPP文件可以在VC++6.0里直接编译运行

程序A:
//---------------------------------------------------------------------------------------------------------------------
#include<iostream>
#include<string>
#include<math.h>
using namespace std;
//采用小素数表,以及选择恰当的上界以及步长可以提高效率

//小素数表,可以减少无用试探,规模可以自己决定
const int littlePrime[12] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37 };

//问题类
class GoldbachConjecture
{
public:
    //判断num是否是素数
    bool isPrime(int num)
    {
      if( num <= 1 )
      {
        return false;//小于等于0时当然不是素数
      }
      int i,j,max;
      max = int( sqrt(num) );//最大试探值,num如果能被整除,则可以写为m*n,如果m<=n
                    //则显然有m<=sqrt(num),以sqrt(num)可以免掉后半段的重复试探
      //先从质数表开始
      for( i = 0; i<12 && littlePrime<=max; i++ )
      {
        if( num % littlePrime == 0 )//可以被整除,是合数,返回false
            return false;
      }
      //如果表里的数还没过界,则从41开始试探
      if( i==12 )
      {
        for( j = 41; j<=max; j=j+2 )//显然偶数不用试探了,因为进入这里前提是2不能整除num
        {
            if( num % j == 0 )//可以被整除,是合数,返回false
              return false;
        }
      }
      //试探完,可以确定num是素数
      return true;
    }
    bool getPrimePair(int num)//找出满足条件的素数对,找不到输出0
    {
      if( num<=3 )//小于等于3显示无法找到,输出0
      {
        cout<<0<<endl;
        return false;
      }
      if( num%2 == 1 )//奇数可以迅速处理,其中一个数一定是2,因为只有2是偶素数
      {
        if(isPrime(num-2)) //num-2是素数,则2 num-2是满足条件的素数对,显然2最小
        {
            cout<<2<<' '<<num-2<<endl;
            return true;
        }
        else//否则一定没有,直接输出0
        {
            cout<<0<<endl;
            return false;
        }
      }
      //剩下就是处理关键的大偶数了
      int i,j,k,max;
      max = num/2;//只用试前半,后半是重复的,由于是和,取num/2
      //从素数表开始试探
      for( i = 0; i<12&&littlePrime<=max; i++ )
      {
        j = num - littlePrime;
        if( isPrime(j) )//素数表里的已经是素数,只用判断j是否为素数
        {
            //j同时也是素数,由于从小到大试探,且上限定为num/2
            //则肯定满足输出对前一个数不大于后一个数
            //并且前一个数是满足条件的所有素数对中最小的数
            cout<<littlePrime<<' '<<j<<endl;
            return true;
        }
      }
      //超过素数表,从41开始找素数继续试探
      if( i==12 )
      {
        for( k = 41; k<=max; k=k+2 )
        {
            //41往后偶数肯定不是素数,每次+2
            if( isPrime(k) && isPrime(num-k) )
            {
              //找到素数对,输出
              cout<<k<<' '<<(num-k)<<endl;
              return true;
            }
        }
      }
      //过界,可以肯定找不到合题意的素数对
      cout<<0<<endl;
      return false;
    }
};

int main()
{
    int i;
    //建立一个问题对象
    GoldbachConjecture GC;
    //得到数
    cin>>i;
    //输出结果
    GC.getPrimePair(i);
    return 0;
}
//------------------------------------------------------------------------

程序B:
//------------------------------------------------------------------------
#include<iostream>
#include<string>
#include<math.h>
//有n个雷,建立一个有n个结点的图,只有当两个雷距离在5米之内,两个雷的结点之间才有
//边相连,用邻接矩阵存放图,只要从触发雷对应结点开始启遍历该结结点所在的连通分量,
//则所有访问的结点都是可以被触发的
//维护一个访问数组,然后从前往后输出访问标志为ture的结点输出序号即可实现升序输出
using namespace std;
//距离最大值的平方,用来两个雷是否可以互相引爆
const int maxLengthSqrt = 25;

//问题类
class Mines
{
public:
    int numMines; //雷数
    int FirstTrigger; //第一个触发的雷的序号,指的存放图对应序号,为(实际序号-1)
    bool **disGraph;//图的邻接矩阵

    //构造函数,num为雷数,first为第一个触发的雷的序号
    //listMinesPos是一个长度为2*num的数组,每个雷的坐标X,Y存入连续两个单元
    //按照序号依次存入listMinesPos
    Mines(int num, int* listMinesPos, int first )
    {
      numMines = num;
      FirstTrigger = first;
      //分配矩阵空间
      disGraph = new bool* [numMines];
      int i,j;
      for(i=0; i<numMines; i++)
      {
        disGraph = new bool[numMines];
        for(j=0; j<numMines; j++)
        {
            //赋初值
            disGraph[j] = false;
        }
      }
      //任意两个结点计算距离,判断是否有边相连
      for(i=0; i<numMines; i++)
      {
        for( j = i+1; j<numMines; j++ )
        {
            //距离平方<=最大距离平方,则连上一条边
            if(getDisSqrt(listMinesPos[2*i],listMinesPos[2*i+1],
              listMinesPos[2*j],listMinesPos[2*j+1])<=maxLengthSqrt)
            {
              //注意是无向图,对称存放
              //当然可以用三角矩阵压缩存放,不过会增加复杂度和可读性
              disGraph[j] = true;
              disGraph[j] = true;
            }
        }
      }
    }
    ~Mines()
    {
      //记得归还动态分配的空间
      int i;
      for(i=0; i<numMines; i++)
      {
        delete []disGraph;
      }
      delete []disGraph;
    }
    //计算点(x1,y1)和点(x2,y2)两点单距离平方和
    int getDisSqrt(int x1, int y1, int x2, int y2)
    {
      return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
    }
    //升序输出所有可以触发雷
    void printTriggeredMines()
    {
      bool *triggered = new bool[numMines];//触发表,即访问表
      int i,j;
      int *stack = new int[numMines];//建栈,交大轻递归,注意掌握栈来代替递归
      int top = 0;
      for(i=0; i<numMines; i++)
      {
        triggered = false;//赋初值
      }
      stack[top++] = FirstTrigger;//触发雷入栈,开始深度遍历
      while(top>0)
      {
        i = stack[--top];//弹出栈顶
        triggered = true;
        for(j=0;j<numMines;j++)
        {
            if(disGraph[j]==true&&triggered[j]==false)
            {
              stack[top++] = j;//所有未触发邻结点入栈
            }
        }
      }
      bool isFirst = true;//控制输出,第一个数前面不输出空格,其它需要输出空格
      for(i=0; i<numMines; i++)
      {
        if(triggered)
        {
            if(isFirst)
            {
              cout<<i+1;//注意,由于矩阵下标和触发标志表都从0开始,输出序号时要+1
              isFirst = false;
            }
            else
            {
              cout<<' '<<i+1;
            }
        }
      }
      cout<<endl;
      delete []stack;//注意归还空间
      delete []triggered;//养成良好习惯
      return;
    }
};

int main()
{
    int num;
    int* listMinesPos;
    int first;
    cin>>num;//输入雷数
    listMinesPos = new int[2*num];//分配空间
    int i;
    //读入所有雷的坐标
    for(i=0;i<num;i++)
    {
      cin>>listMinesPos[2*i]>>listMinesPos[2*i+1];
    }
    //读入触发雷序号
    cin>>first;
    first--;//由于数组序号从0开始,需要-1校正
    Mines M1(num,listMinesPos,first);//初始化问题实例
    M1.printTriggeredMines();//输出结果
    delete []listMinesPos;//归还空间
    return 0;
}
//-------------------------------------------------------------------------------------

程序C:
#include<iostream>
#include<string>
using namespace std;
//前序序列第一个为树的根
//然后在中序序列中找到这个字母,则其左边的为左子树的中序遍历序列
//右边的为右子树的中序遍历
//同时可以算出左右子树的结点个数
//通过个数可以在前序序列中确定左右子数的前序遍历的位置

const string noResult = "-_-bbb";//无结果输出串
//工作记录结构,用来指示当前步该做什么
struct PreInCharPos
{
    int preStart;//树前序序列在MrBean::preOrder中的开始序号
    int inStart;//树中序序列在MrBean::preOrder中的开始序号
    int Length;//序列升序即结点长度
};

class MrBean
{
public:
    string preOrder;//前序序列
    string inOrder;//中序序列
    string postOrder;//后序列
    int num;//结点数

    MrBean()//构造
    {
      cin>>preOrder>>inOrder;//读入前序和中序序列
      if(preOrder.size()==inOrder.size())
      {
        num = preOrder.size();//结点数即为序列长度
      }
      else
      {
        num = 0;//两者不等,非法串,让num=0来表示
      }
    }

    //parent指示父结点的前序序列和后序序列位置,
    //用LChild和RChild返回左右子树的前序序列和后序序列位置
    bool getChild(PreInCharPos parent, PreInCharPos &LChild, PreInCharPos &RChild)
    {
      //先找出根结点
      char root = preOrder[parent.preStart];
      int i;
      //i定位在中序序列中根的位置
      for(i=parent.inStart;
        (i<parent.inStart+parent.Length)
            &&(inOrder!=root);
        i++);
      if(i==parent.inStart+parent.Length)
        return false;//找不到根,非法序列,返回false
      LChild.Length = i - parent.inStart;//通过i的位置算出左右子树结点数
      RChild.Length = parent.Length - 1 - LChild.Length;
      LChild.inStart = parent.inStart;//中序起始位置
      RChild.inStart = i + 1;
      LChild.preStart = parent.preStart + 1;//后序起始位置
      RChild.preStart = LChild.preStart + LChild.Length;
      return true;//正确返回
    }
   
    //找后序序列,在从根开始逐步分解序列,可以得到反序的后序遍历,从后往前装入postOrder
    //算法可能不好懂,先看后序序列倒装就是先根再右子树再左子树,类似于交换左右子树
    //访问顺序的先序遍历,估且称为变化的先序遍历,用栈实现
    //考交大一定要重点掌握非递归的树的各种遍历
    //也可以先建树再用后序遍历输出结果,但效率就不高了
    bool getPostOrder()
    {
      if( num == 0 )
      {
        return false;//结点为0或者非法串,即两串不等长,肯定无对应后序串,返回false
      }
      bool finish = false;//指示循环是否结束
      postOrder.resize(num);//调整postOrder长度
      PreInCharPos work,lc,rc;//工作空间
      PreInCharPos *stack = new PreInCharPos[num];//建栈分配空间
      int top = 0;//SP
      int postCount = num - 1;//反序完成后序序列
      work.inStart = work.preStart = 0;//整个树的工作状态
      work.Length = num;
      stack[top++] = work;//将树入栈
      while(!finish&&top>0)
      {
        work = stack[--top];
        if(!getChild(work,lc,rc))
            finish = true;//如果无法分出左右子树,则出错,循环结束
        else
        {
            postOrder[postCount--] = preOrder[work.preStart];//将根填入postOrder
            if(lc.Length>0)//左子树不空则入栈
            {
              stack[top++] = lc;
            }
            if(rc.Length>0)//右子树不空则入栈,由于是先右后左遍历,所以右儿子后入栈,先出栈被访问
            {
              stack[top++] = rc;
            }
        }
      }
      delete []stack;//归还空间
      return !finish;//finish=flase则正常遍历完退出循环,得到合法后序序列,返回ture
                //finish=ture是由于分左右子树时失败而退出循环,无法得到
                //合法后序序列,返回fasle.总之返回!finish
    }
   
    //输出结果
    void printPostOrder()
    {
      if(getPostOrder())//求后序
      {
        cout<<postOrder;//成功则输出后序
      }
      else
      {
        cout<<noResult;//否则输出无结果串
      }
      return;
    }
};

int main()
{
    MrBean mb;//构造实例
    mb.printPostOrder();//输出结果
    return 0;
}

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值