目录
1. 格子中输出
题目要求
StringInGrid函数会在一个指定大小的格子中打印指定的字符串。
要求字符串在水平、垂直两个方向上都居中。
如果字符串太长,就截断。
如果不能恰好居中,可以稍稍偏左或者偏上一点。
下面的程序实现这个逻辑,请填写划线部分缺少的代码。
#include <stdio.h>
#include <string.h>
void StringInGrid(int width, int height, const char* s)
{
int i,k;
char buf[1000];
strcpy(buf, s);
if(strlen(s)>width-2) buf[width-2]=0;
printf("+");
for(i=0;i<width-2;i++) printf("-");
printf("+\n");
for(k=1; k<(height-1)/2;k++){
printf("|");
for(i=0;i<width-2;i++) printf(" ");
printf("|\n");
}
printf("|");
printf("%*s%s%*s",_____________________________________________); //填空
printf("|\n");
for(k=(height-1)/2+1; k<height-1; k++){
printf("|");
for(i=0;i<width-2;i++) printf(" ");
printf("|\n");
}
printf("+");
for(i=0;i<width-2;i++) printf("-");
printf("+\n");
}
int main()
{
StringInGrid(20,6,"abcd1234");
return 0;
}
对于题目中数据,应该输出:
+------------------+
| |
| abcd1234 |
| |
| |
+------------------+
注意:只填写缺少的内容,不要书写任何题面已有代码或说明性文字。
(width-2-strlen(buf))/2,"",buf,(width-2-strlen(buf))/2,""
本题难点在于:printf("%*s",5,"") 表示输出5个空格。所以考察的是printf的特殊用法。
2. 九数组分数
题目要求:
1,2,3...9 这九个数字组成一个分数,其值恰好为1/3,如何组法?
注意:只填写缺少的内容,不要书写任何题面已有代码或说明性文字。
下面的程序实现了该功能,请填写划线部分缺失的代码。
#include <stdio.h>
void test(int x[])
{
int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];
int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];
if(a*3==b) printf("%d / %d\n", a, b);
}
void f(int x[], int k)
{
int i,t;
if(k>=9){
test(x);
return;
}
for(i=k; i<9; i++){
{t=x[k]; x[k]=x[i]; x[i]=t;}
f(x,k+1);
_____________________________________________ // 填空处 {t=x[k]; x[k]=x[i]; x[i]=t;}
}
}
int main()
{
int x[] = {1,2,3,4,5,6,7,8,9};
f(x,0);
return 0;
}
一个简单的dfs过程,在数字位置交换完成后,再将交换位置复原。
3.显示二叉树
题目要求:
排序二叉树的特征是:
某个节点的左子树的所有节点值都不大于本节点值。
某个节点的右子树的所有节点值都不小于本节点值。
为了能形象地观察二叉树的建立过程,小明写了一段程序来显示出二叉树的结构来。
#include <stdio.h>
#include <string.h>
#define N 1000
#define HEIGHT 100
#define WIDTH 100 0
struct BiTree
{
int v;
struct BiTree* l;
struct BiTree* r;
};
int max(int a, int b)
{
return a>b? a : b;
}
struct BiTree* init(struct BiTree* p, int v)
{
p->l = NULL;
p->r = NULL;
p->v = v;
return p;
}
void add(struct BiTree* me, struct BiTree* the)
{
if(the->v < me->v){
if(me->l==NULL) me->l = the;
else add(me->l, the);
}
else{
if(me->r==NULL) me->r = the;
else add(me->r, the);
}
}
//获得子树的显示高度
int getHeight(struct BiTree* me)
{
int h = 2;
int hl = me->l==NULL? 0 : getHeight(me->l);
int hr = me->r==NULL? 0 : getHeight(me->r);
return h + max(hl,hr);
}
//获得子树的显示宽度
int getWidth(struct BiTree* me)
{
char buf[100];
sprintf(buf,"%d",me->v);
int w = strlen(buf);
if(me->l) w += getWidth(me->l);
if(me->r) w += getWidth(me->r);
return w;
}
int getRootPos(struct BiTree* me, int x){
return me->l==NULL? x : x + getWidth(me->l);
}
//把缓冲区当二维画布用
void printInBuf(struct BiTree* me, char buf[][WIDTH], int x, int y)
{
int p1,p2,p3,i;
char sv[100];
sprintf(sv, "%d", me->v);
p1 = me->l==NULL? x : getRootPos(me->l, x);
p2 = getRootPos(me, x);
p3 = me->r==NULL? p2 : getRootPos(me->r, p2+strlen(sv));
buf[y][p2] = '|';
for(i=p1; i<=p3; i++) buf[y+1][i]='-';
for(i=0; i<strlen(sv); i++) buf[y+1][p2+i]=sv[i];
if(p1<p2) buf[y+1][p1] = '/';
if(p3>p2) buf[y+1][p3] = '\\';
if(me->l) printInBuf(me->l,buf,x,y+2);
if(me->r) ________________________;//填空位置 printInBuf(me>r,buf,p2+strlen(sv),y+2)
}
void showBuf(char x[][WIDTH])
{
int i,j;
for(i=0; i<HEIGHT; i++){
for(j=WIDTH-1; j>=0; j--){
if(x[i][j]==' ') x[i][j] = '\0';
else break;
}
if(x[i][0]) printf("%s\n",x[i]);
else break;
}
}
void show(struct BiTree* me)
{
char buf[HEIGHT][WIDTH];
int i,j;
for(i=0; i<HEIGHT; i++)
for(j=0; j<WIDTH; j++) buf[i][j] = ' ';
printInBuf(me, buf, 0, 0);
showBuf(buf);
}
int main()
{
struct BiTree buf[N]; //存储节点数据
int n = 0; //节点个数
init(&buf[0], 500); n++; //初始化第一个节点
add(&buf[0], init(&buf[n++],200)); //新的节点加入树中
add(&buf[0], init(&buf[n++],509));
add(&buf[0], init(&buf[n++],100));
add(&buf[0], init(&buf[n++],250));
add(&buf[0], init(&buf[n++],507));
add(&buf[0], init(&buf[n++],600));
add(&buf[0], init(&buf[n++],650));
add(&buf[0], init(&buf[n++],450));
add(&buf[0], init(&buf[n++],440));
add(&buf[0], init(&buf[n++],220));
show(&buf[0]);
return 0;
}
对于上边的测试数据,应该显示出:
|
/--------------500---\
| |
/--200---\ /--509\
| | | |
100 /--250---\ 507 600\
| | |
220 /--450 650
|
440
4. 快速排序
题目要求:
排序在各种场合经常被用到。
快速排序是十分常用的高效率的算法。
其思想是:先选一个“标尺”,
用它把整个队列过一遍筛子,
以保证:其左边的元素都不大于它,其右边的元素都不小于它。
这样,排序问题就被分割为两个子区间。
再分别对子区间排序就可以了。
下面的代码是一种实现,请分析并填写划线部分缺少的代码。
#include <stdio.h>
void swap(int a[], int i, int j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
int partition(int a[], int p, int r)
{
int i = p;
int j = r + 1;
int x = a[p];
while(1){
while(i<r && a[++i]<x);
while(a[--j]>x);
if(i>=j) break;
swap(a,i,j);
}
______________________;//swap(a,p,j)
return j;
}
void quicksort(int a[], int p, int r)
{
if(p<r){
int q = partition(a,p,r);
quicksort(a,p,q-1);
quicksort(a,q+1,r);
}
}
int main()
{
int i;
int a[] = {5,13,6,24,2,8,19,27,6,12,1,17};
int N = 12;
quicksort(a, 0, N-1);
for(i=0; i<N; i++) printf("%d ", a[i]);
printf("\n");
return 0;
}
5.消除尾一
题目要求:
下面的代码把一个整数的二进制表示的最右边的连续的1全部变成0
如果最后一位是0,则原数字保持不变。
如果采用代码中的测试数据,应该输出:
00000000000000000000000001100111 00000000000000000000000001100000
00000000000000000000000000001100 00000000000000000000000000001100
请仔细阅读程序,填写划线部分缺少的代码。
#include <stdio.h>
void f(int x)
{
int i;
for(i=0; i<32; i++) printf("%d", (x>>(31-i))&1);
printf(" ");
x = _______________________;//x&(x+1)
for(i=0; i<32; i++) printf("%d", (x>>(31-i))&1);
printf("\n");
}
int main()
{
f(103);
f(12);
return 0;
}
x&(x-1)就是把x的二进制-1,或者说把x的二进制最后一个1变成0。
(c>>(31-i)) & 1 可以分解成:
[1] 31-i 减法,31减去1。
[2] c>>(31-i) c 按2进制数值 右移 (31-i) 位
[3] 右移 后的结果 与 1 做 “按位与” 计算,
显然 c 按2进制数值 右移 (31-i) 位 后 如果 最右一位 是 1,结果输出 1 ,如果 最右一位 是 0,结果输出 0 。
6.打靶
题目要求:
小明参加X星球的打靶比赛。
比赛使用电子感应计分系统。其中有一局,小明得了96分。
这局小明共打了6发子弹,没有脱靶。
但望远镜看过去,只有3个弹孔。
显然,有些子弹准确地穿过了前边的弹孔。
不同环数得分是这样设置的:
1,2,3,5,10,20,25,50
那么小明的6发子弹得分都是多少呢?有哪些可能情况呢?
下面的程序解决了这个问题。
仔细阅读分析代码,填写划线部分缺失的内容。
#include <stdio.h>
#define N 8
void f(int ta[], int da[], int k, int ho, int bu, int sc)
{
int i,j;
if(ho<0 || bu<0 || sc<0) return;
if(k==N){
if(ho>0 || bu>0 || sc>0) return;
for(i=0; i<N; i++){
for(j=0; j<da[i]; j++)
printf("%d ", ta[i]);
}
printf("\n");
return;
}
for(i=0; i<=bu; i++){
da[k] = i;
f(ta, da, k+1, _____________ , bu-i, sc-ta[k]*i); //填空位置 (i==0) ? ho : ho-1
}
da[k] = 0;
}
int main()
{
int ta[] = {1,2,3,5,10,20,25,50};
int da[N];
f(ta, da, 0, 3, 6, 96);
return 0;
}
于代码填空题我的分析一般就是通过凑的方式来解题,因为一般解题的框架都出来了此时你只需要根据解题的描述进一步填空就行了。
对于本题来说主要就在于对每个参数的分析,
f(ta, da, 0, 3, 6, 96);
这一句话几乎把所有的关键内容都给解释了ta与da通过上面的定义可以看出ta为
不同环数得分是这样设置的:1,2,3,5,10,20,25,50
然后通过
for(int j=0; j<da[i]; j++)
1
并且在加上da的长度
int[] da = new int[8];
1
便可以推测出来da存储的为每个分数出现的次数
如果有dfs全排列的基础的话,可以很容易看出来k代表当前位置,相当于一个游标,对于ho刚开始可能不容易看出来,因为它也是将来填空要填的内容,所以可以先忽略。
然后看bu,通过上一部分析得出的da存储的为每个分数出现的次数,并且bu的初始值为6在与题干比较
这局小明共打了6发子弹
就推断出了bu是剩下的子弹数
同理sc的初值为96很容易看出sc代表分数。
分析完关键变量后下一步主要就是简单分析代码,不需要读懂,但要基本明白其所讲内容,通过一个数组并且加递归,可以看出其主要运用的就是全排列的思想,把每个环所有可能的次数都过滤一遍,并且保留下来的都是刚好bu==0 ho==0 sc==0的
if (ho < 0 || bu < 0 || sc < 0)
return;
if (ho > 0 || bu > 0 || sc > 0)
return;
所以便推断出ho一定是向下减少的,并且最终减为0,而且ho的初值为3,与题干中的只有3个弹孔刚好重和,所以ho代表的就是剩下的弹孔数,而这个弹孔数其实就是da中不为0的数的个数
比如da中存储的为0 0 2 0 0 2 2 0
则其不为0的为三个刚刚好,所以关键在于怎么看da中不为0的,因为代码只能填一个空,所以不能进行遍历,所以可以采用边进行边统计的方法,因为是看da是否为0为0
7. 字母组串
题目要求:
由 A,B,C 这3个字母就可以组成许多串。
比如:"A","AB","ABC","ABA","AACBB" ....
现在,小明正在思考一个问题:
如果每个字母的个数有限定,能组成多少个已知长度的串呢?
他请好朋友来帮忙,很快得到了代码,
解决方案超级简单,然而最重要的部分却语焉不详。
请仔细分析源码,填写划线部分缺少的内容。
#include <stdio.h>
// a个A,b个B,c个C 字母,能组成多少个不同的长度为n的串。
int f(int a, int b, int c, int n)
{
if(a<0 || b<0 || c<0) return 0;
if(n==0) return 1;
return ________________________ ; // 填空 f(a-1,b,c,n-1)+f(a,b-1,c,n-1)+f(a,b,c-1,n-1)
}
int main()
{
printf("%d\n", f(1,1,1,2));
printf("%d\n", f(1,2,3,3));
return 0;
}
对于上面的测试数据,小明口算的结果应该是:
6
19
8. 最大公共子串
题目要求:
最大公共子串长度问题就是:
求两个串的所有子串中能够匹配上的最大长度是多少。
比如:"abcdkkk" 和 "baabcdadabc",
可以找到的最长的公共子串是"abcd",所以最大公共子串长度为4。
下面的程序是采用矩阵法进行求解的,这对串的规模不大的情况还是比较有效的解法。
请分析该解法的思路,并补全划线部分缺失的代码。
#include <stdio.h>
#include <string.h>
#define N 256
int f(const char* s1, const char* s2)
{
int a[N][N];
int len1 = strlen(s1);
int len2 = strlen(s2);
int i,j;
memset(a,0,sizeof(int)*N*N);
int max = 0;
for(i=1; i<=len1; i++){
for(j=1; j<=len2; j++){
if(s1[i-1]==s2[j-1]) {
a[i][j] = __________________________; //填空a[i-1][j-1] + 1
if(a[i][j] > max) max = a[i][j];
}
}
}
return max;
}
int main()
{
printf("%d\n", f("abcdkkk", "baabcdadabc"));
return 0;
}
9. 表达式计算
题目要求:
虽然我们学了许久的程序设计,但对于简单的四则混合运算式,如果让我们完全白手起家地编程来解析,还是有点棘手。
这里,我们简化一下问题,假设只有加法和乘法,并且没有括号来改变优先级。
再假设参加运算的都是正整数。
在这么多的限制条件下,表达式的解析似乎简单了许多。
下面的代码解决了这个问题。请仔细阅读源码,并填写划线部分缺少的代码。
#include <stdio.h>
int f3(const char* s, int begin, int end)
{
int sum = 0;
int i;
for(i=begin; i<end; i++){
if(s[i]==' ') continue;
sum = sum * 10 + (s[i]-'0');
}
return sum;
}
int f2(const char* s, int begin, int end)
{
int p = begin;
int pro = 1;
while(1){
int p0 = p;
while(p!=end && s[p]!='*') p++;
pro *= _______________________________; //填空 f3(s,p0,p)
if(p==end) break;
p++;
}
printf("f2: pro=%d\n", pro);
return pro;
}
int f(const char* s)
{
int p = 0;
int sum = 0;
while(1){
int p0 = p;
while(s[p]!=0 && s[p]!='+') p++;
sum += f2(s,p0,p);
if(s[p]==0) break;
p++;
}
return sum;
}
int main()
{
int x = f("12+18+5*4*3+10");
printf("%d\n", x);
return 0;
}
10. 打印图形
题目要求:
如下的程序会在控制台绘制分形图(就是整体与局部自相似的图形)。
当n=1,2,3的时候,输出如下:
请仔细分析程序,并填写划线部分缺少的代码。
n=1时:
o
ooo
o
n=2时:
o
ooo
o
o o o
ooooooooo
o o o
o
ooo
o
n=3时:
o
ooo
o
o o o
ooooooooo
o o o
o
ooo
o
o o o
ooo ooo ooo
o o o
o o o o o o o o o
ooooooooooooooooooooooooooo
o o o o o o o o o
o o o
ooo ooo ooo
o o o
o
ooo
o
o o o
ooooooooo
o o o
o
ooo
o
源程序:
#include <stdio.h>
#include <stdlib.h>
void show(char* buf, int w){
int i,j;
for(i=0; i<w; i++){
for(j=0; j<w; j++){
printf("%c", buf[i*w+j]==0? ' ' : 'o');
}
printf("\n");
}
}
void draw(char* buf, int w, int x, int y, int size){
if(size==1){
buf[y*w+x] = 1;
return;
}
int n = _________________________ ; //填空 size/3
draw(buf, w, x, y, n);
draw(buf, w, x-n, y ,n);
draw(buf, w, x+n, y ,n);
draw(buf, w, x, y-n ,n);
draw(buf, w, x, y+n ,n);
}
int main()
{
int N = 3;
int t = 1;
int i;
for(i=0; i<N; i++) t *= 3;
char* buf = (char*)malloc(t*t);
for(i=0; i<t*t; i++) buf[i] = 0;
draw(buf, t, t/2, t/2, t);
show(buf, t);
free(buf);
return 0;
}