初学acmer--读《算法竞赛入门经典》笔记<四>(p36-41)

本文探讨了三个有趣的C语言编程挑战:解析浮点数循环问题、模拟开灯问题及蛇形填数游戏。通过实例代码展示了如何巧妙地解决这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.下面的程序运行结果是什么?(p36

#include<stdio.h>
int main() 
{
	double i;
	for(i=9.9;i!=10;i+=0.1)
	{
		printf("%.lf\n",i);
	}
	return 0;
}
根据自己主观感觉,以为结果是9.9

运行发现是

10
10
10
10
10
10
10
11
11
11
11
11
11
11
11
11
11
12
12
12
。。。。

无限循环下去了

再仔细思索 发现两个问题

①double类型的数值进行运算得不到“数学上精确”的结果(详见点击打开链接

②按%.lf输出,结果是忽略小数点后面的部分的

而按%x.ylf输出的话,结果是总共占x位,保留y位小数

接着稍稍改了代码,将for语句中的初始化语句改成i=10,运行结果不再无限循环了,也没输出



2.开灯问题(p39)

题目: 有n盏灯,编号为1~n。第一个人把所有灯打开,第二个人按下所有编号为2的倍数的开关(这些灯将被关掉),第三个按下所有编号为3的倍数的开关(其中关掉的灯将被打开,开着的灯将被关掉)。以此类推,一共有k个人,问最后有哪些灯开着?输入n和k,输出开着的灯的编号。k<=n<=1000
样例输入:
7 3
样例输出:
1 5 6 7


分析:此题有一个特点:若数量较少时,人可以不借助任何外物就可以解决,但一旦数量过大,单凭人力就有些吃力了,所以就可以借助计算机超凡的计算力来“模拟”问题情境,从而解决问题。用a[1],a[2],a[3],... ,a[n]表示编号为1,2,3,...,n的灯是否开着,开闭状态可以用0和1表示。开关动作可以用取反来模拟(!0=1,!1=0)

代码如下:

#include<stdio.h>
#include<string.h>               //下面的memset函数所需的头文件
#define maxn 1010
int a[maxn];
int main()
{
	int n,k,first=1;
	memset(a,0,sizeof(a));     //将数组a中的元素初始化为0
	scanf("%d%d",&n,&k);
	for(int i=1;i<=k;i++)
	  for(int j=1;j<=n;j++)
	    if(j%i==0)  a[j]=!a[j];
   for(int i=1;i<=n;i++)
   if(a[i]) {
   	if(first)
   	first=0;                //输出时保持空格,即除了第一个数,其余所有的数输出之前都先输出一个空格
   	else
   	printf(" ");
   	printf("%d",i);
   }
   printf("\n");
   return 0;
}

3.蛇形填数(P39)

题目:在n*n方阵里填入1,2,。。。,n*n,要求填成蛇形。例如,n=4时方阵为:

10 11 12 1

 9 16 13 2

 8 15 14 3

 7  6    5  4(方阵中,多余的空格不必严格输出。n<=8)

分析:同上一题特点一样,此题可以通过“模拟”来解。仔细分析,“笔”的移动轨迹为:下,下,下,左,左,左,上,上,上,右,右,下,下,左,上

容易发现都是四个过程一个循环(先下,再左,再上,最后右)。而且每次转向无非两种情况:一是再走就出界(如从4到5),二是再走就要走到以前走的格子(如从12到13)

第一种情况可以轻易判断,而第二种情况可以通过先将二维数组全部初始化为0,通过数组中元素是否为零判断是否已经填过


代码如下:

#include<stdio.h>
#include<string.h>
#define maxn 20
int a[maxn][maxn];
int main()
{
	int n,x,y,tot=0;
	scanf("%d",&n);
	memset(a,0,sizeof(a));
	x=0;
	y=n-1;
	a[x][y]=1;
	tot=1;
	while(tot<n*n)
	{
		while(x+1<n&&!a[x+1][y])  a[++x][y]=++tot;
		while(y-1>=0&&!a[x][y-1])  a[x][--y]=++tot;
		while(x-1>=0&&!a[x-1][y])   a[--x][y]=++tot;
		while(y+1<n&&!a[x][y+1])   a[x][++y]=++tot;
	}
	for(int i=0;i<n;i++)
	 {
 		 for(int j=0;j<n;j++)
		  printf("%3d",a[i][j]);
		  printf("\n");
	 }
	 return 0;
}
ps:四条while语句都遵循一个原则,就是先判断,后赋值,而不是赋完值再判断,因为若是出错,很难修改回来

提示:很多情况下,最好是在做一件事之前检查是不是可以做,而不要做完再后悔,因为“悔棋”往往比较麻烦





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值