北京大学 2015研究生上机测试 题解
A:细胞分裂
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
医生在显微镜下进行某细胞实验。已知细胞分裂的速度是每秒一次,而实验需要一定量的细胞才能够进行。请你计算一下需要多少时间才能从一个细胞得到所需的细胞数量。
例如:从1个细胞开始,经过1秒钟,得到2个细胞;从1个细胞开始,经过2秒钟,得到4个细胞;从1个细胞开始,经过3秒钟,得到8个细胞,....。 - 输入
- 输入是一个整数n,代表所需细胞数
- 输出
- 输出为一行,代表需要的时间(单位秒,整数格式)
- 样例输入
-
5000
- 样例输出
-
13
//A题 #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> using namespace std; int main() { long n; int t; while(scanf("%ld", &n)!=EOF){ t=0; long cur=1; while(cur<n) { t++; cur=cur*2; } printf("%d\n", t); } return 0; }
B:TEX Quotes
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
- TEX is a typesetting language developed by Donald Knuth. It takes source text together with a few typesetting instructions and produces, one hopes, a beautiful document. Beautiful documents use double-left-quote and double-right-quote to delimit quotations, rather than the mundane " which is what is provided by most keyboards. Keyboards typically do not have an oriented double-quote, but they do have a left-single-quote ` and a right-single-quote '. Check your keyboard now to locate the left-single-quote key ` (sometimes called the "backquote key") and the right-single-quote key ' (sometimes called the "apostrophe" or just "quote"). Be careful not to confuse the left-single-quote ` with the "backslash" key \. TEX lets the user type two left-single-quotes `` to create a left-double-quote and two right-single-quotes '' to create a right-double-quote. Most typists, however, are accustomed to delimiting their quotations with the un-oriented double-quote ".
If the source contained
"To be or not to be," quoth the bard, "that is the question."
then the typeset document produced by TEX would not contain the desired form: "To be or not to be," quoth the bard, "that is the question." In order to produce the desired form, the source file must contain the sequence:
``To be or not to be,'' quoth the bard, ``that is the question.''
You are to write a program which converts text containing double-quote (") characters into text that is identical except that double-quotes have been replaced by the two-character sequences required by TEX for delimiting quotations with oriented double-quotes. The double-quote (") characters should be replaced appropriately by either `` if the " opens a quotation and by '' if the " closes a quotation. Notice that the question of nested quotations does not arise: The first " must be replaced by ``, the next by '', the next by ``, the next by '', the next by ``, the next by '', and so on. - 输入
- Input will consist of several lines of text containing an even number of double-quote (") characters. Input is ended with an end-of-file character.
- 输出
- The text must be output exactly as it was input except that:
- the first " in each pair is replaced by two ` characters: `` and
- the second " in each pair is replaced by two ' characters: ''.
- 样例输入
-
"To be or not to be," quoth the Bard, "that is the question". The programming contestant replied: "I must disagree. To `C' or not to `C', that is The Question!"
- 样例输出
-
``To be or not to be,'' quoth the Bard, ``that is the question''. The programming contestant replied: ``I must disagree. To `C' or not to `C', that is The Question!''
代码:#include <stdio.h> #include <string.h> int main() { char ch; bool ff; ff=false; while(ch=getchar()){ if(ch=='\"') { if(ff==false){ printf("``"); ff=true; } else{ printf("''"); ff=false; } } else printf("%c", ch); } return 0; }
C:二维数组回形遍历
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
给定一个row行col列的整数数组array,要求从array[0][0]元素开始,按回形从外向内顺时针顺序遍历整个数组。如图所示:
- 输入
- 输入的第一行上有两个整数,依次为row和col。
余下有row行,每行包含col个整数,构成一个二维整数数组。
(注:输入的row和col保证0 < row < 100, 0 < col < 100) - 输出
- 按遍历顺序输出每个整数。每个整数占一行。
- 样例输入
-
4 4 1 2 3 4 12 13 14 5 11 16 15 6 10 9 8 7
- 样例输出
-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 代码:
//C题 #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> using namespace std; int main() { int row, col; int i, j; int map[110][110]; bool vis[110][110]; while(scanf("%d %d", &row, &col)!=EOF) { for(i=0; i<row; i++){ for(j=0; j<col ;j++) { scanf("%d", &map[i][j] ); } } //建图 memset(vis, false, sizeof(vis)); int cnt=row*col; int step=1; int x=0, y=0; int n=row; int m=col; printf("%d\n", map[0][0]); vis[0][0]=true; while(step < cnt ) { while(y+1<m && vis[x][y+1]==false){ printf("%d\n", map[x][++y]); step++; vis[x][y]=true; } while(x+1<n && vis[x+1][y]==false){ printf("%d\n", map[++x][y]); step++; vis[x][y]=true; } while(y>0 && vis[x][y-1]==false){ printf("%d\n", map[x][--y]); step++; vis[x][y]=true; } while(x>0 && vis[x-1][y]==false){ printf("%d\n", map[--x][y]); step++; vis[x][y]=true; } } } return 0; }
D:Function Run Fun
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
- We all love recursion! Don't we?
Consider a three-parameter recursive function w(a, b, c):
if a <= 0 or b <= 0 or c <= 0, then w(a, b, c) returns:
1
if a > 20 or b > 20 or c > 20, then w(a, b, c) returns:
w(20, 20, 20)
if a < b and b < c, then w(a, b, c) returns:
w(a, b, c-1) + w(a, b-1, c-1) - w(a, b-1, c)
otherwise it returns:
w(a-1, b, c) + w(a-1, b-1, c) + w(a-1, b, c-1) - w(a-1, b-1, c-1)
This is an easy function to implement. The problem is, if implemented directly, for moderate values of a, b and c (for example, a = 15, b = 15, c = 15), the program takes hours to run because of the massive recursion. - 输入
- The input for your program will be a series of integer triples, one per line, until the end-of-file flag of -1 -1 -1. Using the above technique, you are to calculate w(a, b, c) efficiently and print the result.
- 输出
- Print the value for w(a,b,c) for each triple.
- 样例输入
-
1 1 1 2 2 2 10 4 6 50 50 50 -1 7 18 -1 -1 -1
- 样例输出
-
w(1, 1, 1) = 2 w(2, 2, 2) = 4 w(10, 4, 6) = 523 w(50, 50, 50) = 1048576 w(-1, 7, 18) = 1
- 来源
- Pacific Northwest 1999
- 算法分析:
- 正如题目所说的,如果按照题目里的做法,当数据稍微大一些的时候,计算的过程就递归起来计算时间太长了。
- 如何想要快速的获得结果呢?仔细读题目要求可知,当a, b, c其中任何一个元素大于20时,就返回w(20,20,20)。
- 也就是说,我只要打表存储好了w(0,0,0)--->w(20,20,20)的值就好了。
- 打表的算法,只需要稍微修改一下递归的计算过程就行了。
- 代码:
-
#include <stdio.h> #include <string.h> int dd[25][25][25]; int calc(int a, int b, int c){ if(a<=0 || b<=0 || c<=0 ) return 1; else if(a>20 || b>20 || c>20) return dd[20][20][20]; else if(a<b && b<c) return dd[a][b][c-1]+dd[a][b-1][c-1]-dd[a][b-1][c]; else return dd[a-1][b][c]+dd[a-1][b-1][c]+dd[a-1][b][c-1]-dd[a-1][b-1][c-1]; } int main() { int a, b, c; int i, j, k; for(i=0; i<=20; i++){ for(j=0; j<=20; j++){ dd[0][i][j]=1; dd[i][0][j]=1; dd[i][j][0]=1; } }//三维数组存储表的初始化 for(i=1; i<=20; i++){ for(j=1; j<=20; j++){ for(k=1; k<=20; k++){ dd[i][j][k] = calc(i, j, k); } } }//三维表的元素计算 while(scanf("%d %d %d", &a, &b, &c)!=EOF){ if(a==-1 && b==-1 && c==-1 ){ break;//程序结束标志 } else if(a<=0 || b<=0 || c<=0 ){ printf("w(%d, %d, %d) = ", a, b, c); printf("1\n");//程序特判条件 } else if(a>20 || b>20 || c>20){ printf("w(%d, %d, %d) = ", a, b, c); printf("1048576\n");//程序特判条件 } else{ printf("w(%d, %d, %d) = ", a, b, c); printf("%d\n", dd[a][b][c] ); } } return 0; }
E:区间合并
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
给定 n 个闭区间 [ai; bi],其中i=1,2,...,n。任意两个相邻或相交的闭区间可以合并为一个闭区间。例如,[1;2] 和 [2;3] 可以合并为 [1;3],[1;3] 和 [2;4] 可以合并为 [1;4],但是[1;2] 和 [3;4] 不可以合并。
我们的任务是判断这些区间是否可以最终合并为一个闭区间,如果可以,将这个闭区间输出,否则输出no。
- 输入
- 第一行为一个整数n,3 ≤ n ≤ 50000。表示输入区间的数量。
之后n行,在第i行上(1 ≤ i ≤ n),为两个整数 ai 和 bi ,整数之间用一个空格分隔,表示区间 [ai; bi](其中 1 ≤ ai ≤ bi ≤ 10000)。 - 输出
- 输出一行,如果这些区间最终可以合并为一个闭区间,输出这个闭区间的左右边界,用单个空格隔开;否则输出 no。
- 样例输入
-
5 5 6 1 5 10 10 6 9 8 10
- 样例输出
-
1 10
代码:#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; struct node { int a; int b; bool operator <(const node &x)const{ if(a==x.a) return b<x.b; else return a<x.a; } }q[50010]; int main() { int n; int i, j; while(scanf("%d", &n)!=EOF) { for(i=0; i<n; i++) { scanf("%d %d", &q[i].a, &q[i].b ); } sort(q, q+n); bool flag=true; int zuo, you; zuo=q[0].a; you=q[0].b; for(i=1; i<n; i++) { if(q[i].a <= you) { if(q[i].b > you) { you=q[i].b; } } else{ flag=false; break; } } if(flag){ printf("%d %d\n", zuo, you); } else printf("no\n"); } return 0; }
F:由中根序列和后根序列重建二叉树
- 总时间限制:
- 500ms
- 内存限制:
- 65535kB
- 描述
-
我们知道如何按照三种深度优先次序来周游一棵二叉树,来得到中根序列、前根序列和后根序列。反过来,如果给定二叉树的中根序列和后根序 列,或者给定中根序列和前根序列,可以重建一二叉树。本题输入一棵二叉树的中根序列和后根序列,要求在内存中重建二叉树,最后输出这棵二叉树的前根序列。
用不同的整数来唯一标识二叉树的每一个结点,下面的二叉树
中根序列是9 5 32 67
后根序列9 32 67 5
前根序列5 9 67 32
- 输入
- 两行。第一行是二叉树的中根序列,第二行是后根序列。每个数字表示的结点之间用空格隔开。结点数字范围0~65535。暂不必考虑不合理的输入数据。
- 输出
- 一行。由输入中的中根序列和后根序列重建的二叉树的前根序列。每个数字表示的结点之间用空格隔开。
- 样例输入
-
9 5 32 67 9 32 67 5
- 样例输出
-
5 9 67 32
为了便于做题,我写的代码对题目做了稍稍修改,每次先读入一个数字n代表中序和后序有n个元素。
接下来分别读入中序和后序序列。
递归的做法如下:#include <stdio.h> #include <string.h> void build(int len, int *s1, int *s2, int *s) { int p; int i; if(len<=0) return; else{ for(i=0; i<len; i++){ if(s1[i]==s2[len-1]){ p = i; } } build(p, s1, s2, s+1); build(len-p-1, s1+p+1, s2+p, s+p+1); s[0] = s2[len-1]; } } int main() { int i; int s1[1000], s2[1000], s3[1000]; int n; while(scanf("%d", &n)!=EOF) { for(i=0; i<n; i++){ scanf("%d", &s1[i] ); } for(i=0; i<n; i++){ scanf("%d", &s2[i] ); } build(n, s1, s2, s3 ); for(i=0; i<n; i++){ printf("%d%c", s3[i], i==n-1?'\n':' ' ); } } return 0; }
G:献给阿尔吉侬的花束
- 总时间限制:
- 100ms
- 内存限制:
- 65536kB
- 描述
-
阿尔吉侬是一只聪明又慵懒的小白鼠,它最擅长的就是走各种各样的迷宫。今天它要挑战一个非常大的迷宫,研究员们为了鼓励阿尔吉侬尽快到达终点,就在终点放了一块阿尔吉侬最喜欢的奶酪。现在研究员们想知道,如果阿尔吉侬足够聪明,它最少需要多少时间就能吃到奶酪。
迷宫用一个R×C的字符矩阵来表示。字符S表示阿尔吉侬所在的位置,字符E表示奶酪所在的位置,字符#表示墙壁,字符.表示可以通行。阿尔吉侬在1个单位时间内可以从当前的位置走到它上下左右四个方向上的任意一个位置,但不能走出地图边界。
- 输入
- 第一行是一个正整数T(1 <= T <= 10),表示一共有T组数据。
每一组数据的第一行包含了两个用空格分开的正整数R和C(2 <= R, C <= 200),表示地图是一个R×C的矩阵。
接下来的R行描述了地图的具体内容,每一行包含了C个字符。字符含义如题目描述中所述。保证有且仅有一个S和E。 - 输出
- 对于每一组数据,输出阿尔吉侬吃到奶酪的最少单位时间。若阿尔吉侬无法吃到奶酪,则输出“oop!”(只输出引号里面的内容,不输出引号)。每组数据的输出结果占一行。
- 样例输入
-
3 3 4 .S.. ###. ..E. 3 4 .S.. .E.. .... 3 4 .S.. #### ..E.
- 样例输出
-
5 1 oop!
这是一道考察图论搜索算法的题目,n和m的范围都在200以内,数据量不是很大,完全可以用
dfs进行暴力搜索。我用dfs搜索实现的,至于bfs不清楚是否可以实现,感觉可能不行。
代码:#include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <math.h> #include <algorithm> using namespace std; char s[210][210]; bool vis[210][210]; int n, m; int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}}; //移动的四个方向 struct point{ int x, y; }st, e; bool ok(int i, int j) { if(i>=0 && i<n && j>=0 && j<m) return true; else return false; } int mm=0; bool flag; void dfs(int x, int y, int cnt) { int xx, yy; int i, j; for(i=0; i<4; i++){ xx=x+dir[i][0]; yy=y+dir[i][1]; //位置移动 if(ok(xx, yy)&& s[xx][yy]!='#' && !vis[xx][yy]){ cnt++; if(xx==e.x && yy==e.y )//找到终点 { if(flag==false){ mm = cnt; flag=true; } else{ if(mm>cnt) mm = cnt; } } else{ vis[xx][yy]=true; dfs(xx, yy, cnt);//走了一步 cnt--;//回溯前要将步数减1【重点】 vis[xx][yy]=false;//递归回溯的时候解除标记【重点】 } } } } int main() { int t; scanf("%d", &t); int i, j, k; while(t--){ scanf("%d %d%*c", &n, &m); for(i=0; i<n; i++){ scanf("%s", s[i]); } for(i=0; i<n; i++){ for(j=0; j<m; j++){ if(s[i][j]=='S') { st.x=i; st.y=j; } else if(s[i][j]=='E'){ e.x=i; e.y=j; } } }//寻找起点和终点 memset(vis, false, sizeof(vis)); vis[st.x][st.y]=true; flag=false; dfs(st.x, st.y, 0); if(flag==false) printf("oop!\n"); else printf("%d\n", mm); } return 0; }