算法与程序实践习题解答5模拟.docx

上传人:b****7 文档编号:10485930 上传时间:2023-02-13 格式:DOCX 页数:92 大小:128.10KB
下载 相关 举报
算法与程序实践习题解答5模拟.docx_第1页
第1页 / 共92页
算法与程序实践习题解答5模拟.docx_第2页
第2页 / 共92页
算法与程序实践习题解答5模拟.docx_第3页
第3页 / 共92页
算法与程序实践习题解答5模拟.docx_第4页
第4页 / 共92页
算法与程序实践习题解答5模拟.docx_第5页
第5页 / 共92页
点击查看更多>>
下载资源
资源描述

算法与程序实践习题解答5模拟.docx

《算法与程序实践习题解答5模拟.docx》由会员分享,可在线阅读,更多相关《算法与程序实践习题解答5模拟.docx(92页珍藏版)》请在冰豆网上搜索。

算法与程序实践习题解答5模拟.docx

算法与程序实践习题解答5模拟

《算法与程序实践》习题解答5——模拟

现实中的有些问题,难以找到公式或规律来解决,只能按照一定步骤,不停地做下去,最后才能得到答案。

这样的问题,用计算机来解决十分合适,只要能让计算机模拟人在解决此问题的行为即可。

这一类的问题可以称之为“模拟题”。

比如下面经典的约瑟夫问题:

讲课:

CS51CS52CS53

实验:

CS56CS516CS517

讲课:

CS59CS513CS518

CS51:

约瑟夫问题

(来源:

2746,程序设计导引及在线实践(李文新)例6.1P141)

问题描述:

约瑟夫问题:

有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1开始报数。

就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n,m后,输出最后猴王的编号。

输入:

每行是用空格分开的两个整数,第一个是n,第二个是m(0

最后一行是:

00

输出:

对于每行输入数据(最后一行除外),输出数据也是一行,即最后猴王的编号。

样例输入:

62

124

83

00

样例输出:

5

1

7

解题思路:

初一看,很可能想把这道题目当作数学题来做,即认为结果也许会是以n和m为自变量的某个函数f(n,m),只要发现这个函数,问题就迎刃而解。

实际上,这样的函数很难找,甚至也许根本就不存在。

用人工解决的办法就是将n个数写在纸上排成一圈,然后从1开始数,每数到第m个就划掉一个数,一遍遍做下去,直到剩下最后一个。

有了计算机,这项工作做起来就会快多了,我们只要编写一个程序,模拟人工操作的过程就可以了。

用数组anLoop来存放n个数,相当于n个数排成的圈;用整型变量nPtr指向当前数到的数组元素,相当于人的手指;划掉一个数的操作,就用将一个数组元素置0的方法来实现。

人工数的时候,要跳过已经被划掉的数,那么程序执行的时候,就要跳过为0的数组元素。

需要注意的是,当nPtr指向anLoop中最后一个元素(下标n-1)时,再数下一个,则nPtr要指回到数组的头一个元素(下标0),这样anLoop才象一个圈。

参考程序:

#include

#include

#defineMAX_NUM300

intaLoop[MAX_NUM+1];

intmain()

{

intn,m,i;

while

(1)

{

scanf("%d%d",&n,&m);

if(n==0)break;

for(i=0;i

aLoop[i]=i+1;

intnPtr=0;//存储位置信息

for(i=0;i

{

intnCount=0;//记录本轮数到的猴子数目

while(nCount

{

while(aLoop[nPtr]==0)//跳过已经出圈的猴子

nPtr=(nPtr+1)%n;//到下一个位置,如果到最后就跳到第1个

nCount++;

nPtr=(nPtr+1)%n;

}

nPtr--;//找到要出圈的猴子,位置要回退一个

if(nPtr<0)

nPtr=n-1;

if(i==n-1)//最后一个出圈的猴子

printf("%d\n",aLoop[nPtr]);

aLoop[nPtr]=0;

}

}

return0;

}

注意事项:

上面的程序完全模拟了人工操作的过程,但因为要反复跳过为0的数组元素,因此算法的效率不是很高。

后文的“链表”一章,采用单链表进行模拟来解决本题,就能省去跳过已出圈的猴子这个操作,大大提高了效率。

n个元素的数组,从下标0的元素开始存放猴子编号,则循环报数的时候,下一个猴子的下标就是“(当前猴子下标+1)%n”。

这种写法比用分支语句来决定下个猴子的下标是多少,更快捷而且写起来更方便。

对于本题,虽然很难直接找出结果函数f(n,m),但是如果仔细研究,找出局部的一些规律,比如,每次找下一个要出圈的猴子时,直接根据本次的起点位置就用公式算出下一个要出圈的猴子的位置,那么写出的程序就可以省去数m只猴子这个操作,大大提高效率,甚至不需要用数组来存放n个数。

请写出这个高效而节省空间的程序。

问题一:

在数组里循环计数的时候,一定要小心计算其开始的下标和终止的下标。

比如,语句15,循环是从0到n-1,而不是从0到n。

问题二:

nPtr--到nPtr=n-1回退一个位置,易被忽略或写错。

比如只写了语句nPtr--,忘了处理nPtr变成小于0的情况。

CS52:

花生问题(同CS93)

(来源:

2950,程序设计导引及在线实践(李文新)例4.3P107)

问题描述:

鲁宾逊先生有一只宠物猴,名叫多多。

这天,他们两个正沿着乡间小路散步,突然发现路边的告示牌上贴着一张小小的纸条:

“欢迎免费品尝我种的花生!

——熊字”。

鲁宾逊先生和多多都很开心,因为花生正是他们的最爱。

在告示牌背后,路边真的有一块花生田,花生植株整齐地排列成矩形网格(如图5-1)。

有经验的多多一眼就能看出,每棵花生植株下的花生有多少。

为了训练多多的算术,鲁宾逊先生说:

“你先找出花生最多的植株,去采摘它的花生;然后再找出剩下的植株里花生最多的,去采摘它的花生;依此类推,不过你一定要在我限定的时间内回到路边。

我们假定多多在每个单位时间内,可以做下列四件事情中的一件:

1)从路边跳到最靠近路边(即第一行)的某棵花生植株;

2)从一棵植株跳到前后左右与之相邻的另一棵植株;

3)采摘一棵植株下的花生;

4)从最靠近路边(即第一行)的某棵花生植株跳回路边。

图5-1花生地图5-2摘花生过程

现在给定一块花生田的大小和花生的分布,请问在限定时间内,多多最多可以采到多少个花生?

注意可能只有部分植株下面长有花生,假设这些植株下的花生个数各不相同。

例如在图5-2所示的花生田里,只有位于(2,5),(3,7),(4,2),(5,4)的植株下长有花生,个数分别为13、7、15、9。

沿着图示的路线,多多在21个单位时间内,最多可以采到37个花生。

输入:

输入的第一行包括三个整数,M,N和K,用空格隔开;表示花生田的大小为M*N(1<=M,N<=20),多多采花生的限定时间为K(0<=K<=1000)个单位时间。

接下来的M行,每行包括N个非负整数,也用空格隔开;第i+1行的第j个整数Pij(0<=Pij<=500)表示花生田里植株(i,j)下花生的数目,0表示该植株下没有花生。

输出:

输出包括一行,这一行只包含一个整数,即在限定时间内,多多最多可以采到花生的个数。

样例输入:

6721

0000000

00001300

0000007

01500000

0009000

0000000

样例输出:

37

解题思路:

试图找规律,得到一个以花生矩阵作为自变量的公式来解决这个问题,是不现实的。

结果只能是做了才知道。

即走进花生地,每次要采下一株花生之前,先计算一下,剩下的时间,够不够走到那株花生,采摘,并从那株花生走回到路上。

如果时间够,则走过去采摘;如果时间不够,则采摘活动到此结束。

参考程序:

#include

#include

#include

#include

intT,M,N,K;

#defineMAX_NUM55

intaField[MAX_NUM][MAX_NUM];

intmain()

{

inti,j,t,m,n;

scanf("%d",&T);

for(t=0;t

{

scanf("%d%d%d",&M,&N,&K);

for(m=1;m<=M;m++)

for(n=1;n<=N;n++)

scanf("%d",&aField[m][n]);

intnTotalPeanuts=0;//摘得的花生总数

intnTotalTime=0;//已经花去的总时间

intnCuri=0,nCurj;//当前位置坐标

while(nTotalTime

{

intnMax=0,nMaxi,nMaxj;//最大的花生数目,及其所处的位置

//寻找下一个最大花生数目及其位置

for(i=1;i<=M;i++)

{

for(j=1;j<=N;j++)

{

if(nMax

{

nMax=aField[i][j];

nMaxi=i;

nMaxj=j;

}

}

}

if(nMax==0)//地里没有花生了

break;

if(nCuri==0)

nCurj=nMaxj;//如果当前位置在路上,那么应走到横坐标nMaxj处,再进入花生地

//下面检查剩余的时间够不够走到nMaxi,nMaxj处,摘取花生,并回到路上

if(nTotalTime+nMaxi+1+abs(nMaxi-nCuri)+abs(nMaxj-nCurj)<=K)

{

nTotalTime+=1+abs(nMaxi-nCuri)+abs(nMaxj-nCurj);

nCuri=nMaxi;

nCurj=nMaxj;

nTotalPeanuts+=aField[nMaxi][nMaxj];

aField[nMaxi][nMaxj]=0;//摘走花生赋值为0

}

else

break;

}

printf("%d\n",nTotalPeanuts);

}

return0;

}

实现技巧:

用二维数组存放花生地的信息是很自然的想法。

然而,用aField[0][0]还是aField[1][1]对应花生地的左上角,是值得思考一下的。

因为从地里到路上还需要1个单位时间,题目中的坐标又都是从1开始,所以若aField[1][1]对应花生地的左上角,则从aField[i][j]点,回到路上所需时间就是i,这样更为方便和自然,不易出错。

并不是C/C++的数组下标从0开始,我们使用数组的时候,就要从下标为0的元素开始用。

常见问题:

问题一:

读题时应该仔细读。

有的同学没有看到每次只能拿剩下花生株中最大的,而是希望找到一种在规定时间内能够拿最多花生的组合,把题目变成了另外一道题。

问题二:

有的同学没有读到“没有两株花生株的花生数目相同”的条件,因此把题目复杂化了。

问题三:

这个题目是假设猴子在取花生的过程中不会回到大路上的,有些同学在思考是否可能在中间回到大路上,因为题目没说在大路上移动要花时间,所以有可能中途出来再进去摘的花生更多。

CS53:

显示器(见CS327)

(来源:

2745,程序设计导引及在线实践(李文新)例6.3P147)

问题描述:

你的一个朋友刚买了一台新电脑,之前,他用过的最好的电脑只能是便携式计数器。

现在,你的朋友看着他的新电脑,他很失望,因为他喜欢他的计数器的LC显示器。

因此你决定给你的朋友编写一个程序模拟LC显示器来显示数字。

输入:

输入文件包含多行,每一行为需要显示的数。

每一行中有两个整数s和n,1<=s<=10,0<=n<=99999999。

n为要显示的数值,s是显示的大小。

输入文件的最后一行为两个0,这一行不需要处理。

输出:

以LC显示器方式输出输入文件中的数,用符号“-”表示水平的线段,用符号“|”表示垂直的线段。

数值中的每个数字占s+2列,2s+3行。

在输出时,对每两个数字之间的空白区域,要确保用空格填满,对最后一位数字之后的空白区域,不能输出空格。

每两个数字之间仅有一个空列。

(样例输出中给出了0~9每个数字的输出格式)

每个数值之后输出一个空行。

样例输入:

212345

367890

00

样例输出:

------

||||||

||||||

--------

|||||

|||||

------

---------------

||||||||

||||||||

||||||||

---------

||||||||

||||||||

||||||||

------------

解题分析:

一个计算器上的数字显示单元,可以看作由以下编号从1到7的7个笔画组成:

图5-3显示单元的笔画

那么,我们可以说,数字8覆盖了所有的笔画,数字7覆盖笔画1、3和6,而数字1覆盖笔画3、6。

注意,每个笔画都是由s个’-‘或s个’|’组成。

输出时,先输出第1行,即整数n中所有数字里的笔画1,然后输出第2行到第s+1行,即所有数字的笔画2和笔画3,接下来是第s+2行,即所有数字的笔画4,再接下来是第s+3行到2×s+2行,,就是所有数字的笔画5和笔画6,最后的第2×s+3行,是所有数字的笔画7。

如果某个数字d没有覆盖某个笔画m(m=1…7),那么,输出数字d的笔画m的时候,就应该都输出空格;如果覆盖了笔画m,则输出s个’-‘或s个’|’,这取决于笔画m是横的还是竖的。

由上思路,解决这道题目的关键,就在于如何记录每个数字都覆盖了哪些笔画。

实际上,如果我们记录的是每个笔画都被哪些数字覆盖,则程序实现起来更为容易。

一个笔画被哪些数字所覆盖,可以用一个数组来记录,比如记录笔画1覆盖情况的数组如下:

charn1[11]={"--------"};

其中,n1[i](i=0……9)代表笔画1是否被数字i覆盖。

如果是,则n1[i]为'-',如果否,则n1[i]为空格。

上面的数组的值体现了笔画1被数字0,2,3,5,6,7,8,9覆盖。

对于竖向的笔画2,由字符'|'组成,则记录其覆盖情况的数组如下:

charn2[11]={"||||||"};

该数组的值体现了笔画2被数字0,4,5,6,8,9覆盖。

参考程序:

//显示器(见CS327)2745

#include

#include

charn1[11]={"--------"};//笔画1被数字0,2,3,5,6,7,8,9覆盖

charn2[11]={"||||||"};//笔画2被数字0,4,5,6,8,9覆盖

charn3[11]={"||||||||"};//笔画3被数字0,1,2,3,4,7,8,9覆盖

charn4[11]={"-------"};//笔画4被数字2,3,4,5,6,8,9覆盖

charn5[11]={"||||"};//笔画5被数字0,2,6,8覆盖

charn6[11]={"|||||||||"};//笔画6被数字0,1,3,4,5,6,7,8,9覆盖

charn7[11]={"-------"};//笔画7被数字0,2,3,5,6,8,9覆盖

intmain()

{

ints;

charszNumber[20];

intnDigit,nLength,i,j,k;

while

(1)

{

scanf("%d%s",&s,szNumber);

if(s==0)

break;

nLength=strlen(szNumber);

//输出所有数字的笔画1

for(i=0;i

{

if(i!

=0)printf("");

nDigit=szNumber[i]-'0';

printf("");

for(j=0;j

printf("%c",n1[nDigit]);

printf("");

}

printf("\n");

for(i=0;i

{

for(j=0;j

{

if(j!

=0)printf("");

nDigit=szNumber[j]-'0';

printf("%c",n2[nDigit]);

for(k=0;k

printf("");//笔画2和笔画3之间的空格

printf("%c",n3[nDigit]);

}

printf("\n");

}

for(i=0;i

{

if(i!

=0)printf("");

printf("");

nDigit=szNumber[i]-'0';

for(j=0;j

printf("%c",n4[nDigit]);

printf("");

}

printf("\n");

for(i=0;i

{

for(j=0;j

{

if(j!

=0)printf("");

nDigit=szNumber[j]-'0';

printf("%c",n5[nDigit]);

for(k=0;k

printf("");

printf("%c",n6[nDigit]);

}

printf("\n");

}

for(i=0;i

{

if(i!

=0)printf("");

printf("");

nDigit=szNumber[i]-'0';

for(j=0;j

printf("%c",n7[nDigit]);

printf("");

}

printf("\n");

printf("\n");

}

return0;

}

实现技巧:

一个笔画被哪些数字所覆盖,最直接的想法是用整型数组来记录,比如:

intn1[10]={1,0,1,1,0,1,1,1,1,1};表示笔画1的被覆盖情况。

可是与其在数字i的笔画1所处的位置进行输出的时候,根据n1[i]的值决定输出空格还是'-’,还不如直接用下面的char类型数组来表示覆盖情况:

charn1[11]={"--------"};

这样,在数字i的笔画1所处的位置进行输出的时候,只要输出s个n1[i]就行了。

这是一个很好的思路,它提醒我们,以后在编程时设置一些标志的时候,要考虑一下是否可以直接用更有意义的东西将0,1这样的标志代替。

常见问题:

问题一:

没有注意到输出是按行,即先输出所有数字的第一画,再输出第二画……。

是想一个数字一个数字地从左到右输出,编了一阵才发现不对。

问题二:

忘了输出空格。

应把所有的空白用空格符填充。

例如:

若要输出4的话就是这样:

(。

表示空格)

||

||

--。

问题三:

两组数据之间要加一个空行。

解题分析:

用一个三维数组把0~9共10个数字的字符形式存放起来,这个数组可以定义为digit[10][5][4],第1维10代表10个数字,第2维5代表5行,第3维4代表3列加上一个字符串结束标志。

注意:

两个数字之间有空列,但最后一个数字后没有。

另外最后一个数字后也没有任何空白区域。

参考程序(zzg):

#include

#include

chardigit[10][5][4]={//存放0~9

{"-","||","","||","-"},//0

{"","|","","|",""},//1

{"-","|","-","|","-"},//2

{"-","|","-","|","-"},//3

{"","||","-","|",""},//4

{"-","|","-","|","-"},//5

{"-","|","-","||","-"},//6

{"-","|","","|",""},//7

{"-","||","-","||","-"},//8

{"-","||","-","|","-"}//9

};

intmain()

{

ints;//存储s

charn[10];//用来存储n,要显示的数值

inti,j,k,m;

intlen;//用来存储n的长度

//freopen("in.txt","r",stdin);

//freopen("out.txt","w",stdout);

while

(1)

{

scanf("%d%s",&s,n);

if(s==0)break;

len=strlen(n);

for(i=0;i<5;i++)//输出5行,其中第2行和第4行需要输出s倍

{

if(i==1||i==3)//第2行

{

for(j=1;j<=s;j++)

{

for(k=0;k

{

printf("%c",digit[n[k]-'0'][i][0]);

for(m=1;m<=s;m++)

printf("%c",digit[n[k]-'0'][i][1]);

printf("%c",digit[n[k]-'0'][i][2]);

if(k

}

printf("\n");

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 军事

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1