95005 “请你编程”讲评
思路一:给每个候奖人一个随机的编号,然后按编号从大到小或从小到大选出前N名作为获奖者。
思路二:给每个候奖人一个随机的编号,此编号和候奖人姓名组合成新的字符串。然后比较各字符串的大小以确定前N名获奖者。
思路三:候奖人姓名存放于一个字符串数组中。随机产生N个整数作为数组的下标,根据这些下标就可以索引出全部N名获奖者。
我们来看一下怎样用C语言实现第三种思路。选定义要用到的几个变量:
int t,w,i,r;
char (*name)[9],n[9];
其中T存贮候奖人数,W存贮获奖人数。name被定义为指针,这个指针指向一个字符数组。这个定义比较难理解些,属于比较复杂的指针说明。这时可按如下原则来理解:以标识符为中心,一对方括号一般表示数组,一对圆括号一般表示函数或强调某一优先顺序。方括号对和圆括号对为同一优先级,且比*号为高。如int *(*lpfn)(),lpfn是一个指针,这个指针指向一个函数,这个函数返回一个指向整数的指针。
如下程序段实现由用户输入数据:
printf("请输入候奖人数:");
scanf("%d",&t);
printf("请输入获奖人数:");
scanf("%d",&w);
name=(char(*)[])malloc(t*9*sizeof(char));
for(i=0;i<t;i++){
printf("第%d个姓名为:",i+1);
scanf("%s",name[i]);
}
如下程序段实现随机抽奖:
randomize();
for(i=0;y<w;i++){
r=random(t-i);
strcpy(n,name[r]);
strcpy(name[r],name[t-i-1]);
strcpy(name[t-i-1],n);
}
在for循环体中由0到t-i的随机数就可以确定出一名获奖者。三个strcpy调用实现name[r]和name[t-i-1]的对换,以使获奖者恰位于name数组的末尾。
剩下的工作就是打印获奖者名单,并释放内存:
for(i=t-w;i<t;i++) puts(name[i]);
free(name);
这个程序是由湖北吴云洋同学给出的,它有几个特点:
首先它避开了比较运算,其次是程序设计思路清晰。可清晰地分为输入程序段、抽奖程序段和输出程序段。整个设计是非常优美的。
思路一和思路二都用到了比较或排序操作。我们来看一个Basic程序,它是由广州陆文杰给出的:
INPUT "M,N=",M,N; 输入候奖和获奖人数
DIM A(M),A$(M) 候奖人姓名存于A$[M]中
FOR I=1 TO M
INPUT "Name";A$(I); 输入姓名
RANDOMIZE TIMER
A(I)=FIX(RND*1000000); 产生一个随机号码
A$(I)=STR$(A(I))+"#"+A$(I); 随机号码和姓名
NEXT I ;组合成新字符串
FOR I=1 TO M; 对A$(M)进行排序
FOR J=I+1 TO M
IF VAL (A$(I))<VAL(A$(J))THEN
SWAP A$(I),A$(J)
NEXT J
NEXT I
PRINT "Prize Winner's rotl:";
for I=1 TO N
PRINT A$(I)
NEXT I
END
和前面的C程序比较,这个程序效率低些,因为涉及较多比较操作,而且对整个A$数组进行排序也不太必要,因为一般情况下N远小于M。程序中的常数1000000可设定为一个和候奖人数有关的整数,如100*M。另外,这个程序还有一个潜在的问题,即不同的候奖人完全有可能获得一个相同的号码,所以极有可能出现 Val(A$(I))=VAL(A$(J))但只有A$(I)获奖的情况(I<J)。读者可分析一下前面的C程序,看它是如何避免这一情况的。应该说,那里的处理相当巧妙。
每一期的答卷中都有使用数据库开发语言的,而这一期的题目也特别适合。四川王澎建立了如下的数据库结构:
字段名 类型 宽度
姓名 字符 9
邮编 字符 6
地址 字符 40
获奖等级 字符 10
他写出的抽奖程序可以很容易地加进我们的读者信息管理系统中。
特别应提到的是广东金菊琴设计的多类型抽奖程序。在那里抽奖类型被归为三类:不分等级的抽奖、分等级但候奖人不重复抽奖、分等级且允许重复抽奖。这样的程序通用性就大大提高了。
这一期我们采用了吴云洋设计的电脑抽奖程序,抽奖结果是:
630700 重庆 王澎
200433 上海 程斌
134003 吉林 鄢皓
235038 安徽 丁美怀
510173 广东 金菊琴
246001 安徽 何钧军
226300 通州 郭志坚
518001 中国 林志鹏
545005 广西 温永益
454850 河南 刘卫东