【算法】算法的艺术(六)
报数游戏
n个小孩围成一圈,从1开始报数,报到k的人退出,其余人重新从1报数,仍是报到k退出,直到圈中只剩m个小孩,问最后剩下的是哪些人?
实例解析:
本题在这里将借助于数组求解,用链表求解的方法放在第17章算法与数据结构实例中。
设计思路:用数组元素模拟小孩。定义一个数组,每个元素存入一个数值作为小孩的编号,然后从第一个元素开始报数,报到k的人将编号清0,表示已退出圈子。在报数的过程中,凡是编号为0的人不再参加报数。当数组所有元素都报数后,重新从数组头开始报数,周而复始,直到圈中只剩m个小孩。
#define N 100 int main() {int a[N], n, m, k; int i, count, j; scanf(“%d%d%d”, &n, &k, &m); for(i = 1; i <= n; i++) a[i] = i; j = n; //j:圈中剩余小孩数 count = 0; //为报数做准备,以便第一个小孩报1 i = 1; //从数组的1号元素开始报数 while(j > m){ if(a[i] != 0){ //若小孩未退出圈子 count++; //参加报数 if(count == k){ //若报到k a[i] = 0; //退出圈子 count = 0; //清零,以便重新从1开始报数 j--; //剩余人数减1 } } i++; //下一个小孩准备报数 if(i > n) //若越过最后小孩 i = 1; //转到第一个小孩 } for(i = 1; i <= n; i++) if(a[i] != 0) printf(“%4d”, a[i]); getch(); return 0; }
时钟程序
编写一个程序,显示系统时间,包括小时、分、秒,以及上下午,每秒刷新一次。
实例解析:
本实例需要用到与系统时间有关的一些函数。为方便程序员使用,Turbo C在dos.h中,定义了两个结构体,专门用于时间和日期处理:
struct time{ unsigned char ti_min; //分 unsigned char ti_hour; //小时 unsigned char ti_hund; //百分秒 unsigned char ti_sec; //秒 }; struct date{ int da_year; //自1900年以来的年数 char da_day; //天数 char da_mon; //月数 };
Turbo C还在time.h中提供了一些与时间有关的函数:
(1)char *ctime(const time_t *clock);
作用:把clock所指的时间换成如下格式的字符串:Mon Nov 21 11:31:54 2009\n\0。最后两个字符是换行符和空字符。
(2)char *asctime(const struct tm *t);
作用:把t所指的时间换成如下格式的字符串:Mon Nov 21 11:31:54 2009\n\0。
(3)double difftime(time_t time2, time_t time1);
作用:返回两个时间的差,单位:秒。
(4)struct tm *localtime(long *clock);
作用:把clock所指时间转换为当地标准时间并以tm结构指针的形式返回。
long time(long *p);
作用:给出自1970年1月1日凌晨至今经过的秒数,并将该值写入p所指变量中。
int stime(long *p);
作用:将p所指时间写入计算机中。
而在dos.h中提供了以下与时间有关的函数:
void gettime(struct time *timep);
作用:将计算机系统内的时间写入timep所指结构体变量中。
settime(struct time *timep);
作用:将系统时间设为timep所指的时间。
本例中只用到函数gettime()。
程序代码:
#include <dos.h> #include <conio.h> int main() {struct time curtime; float cur_hour, cur_min, cur_sec; clrscr(); do{ printf("The current time is:\n"); gettime(&curtime); //得到当前时间 if(curtime.ti_hour <= 12) printf("AM "); else{ printf("PM "); curtime.ti_hour -= 12; } if(curtime.ti_hour < 10) printf("0"); printf("%d:", curtime.ti_hour); if(curtime.ti_min < 10) printf("0"); printf("%d:", curtime.ti_min); if(curtime.ti_sec < 10) printf("0"); printf("%d", curtime.ti_sec); sleep(1); //需要conio.h头文件 clrscr(); }while(!kbhit()); return 0; }
简单的计算器(一)
编程,用来计算用户输入的四则运算表达式的值,只含加减乘除,不含括号。
实例解析:
表达式中含有加减乘除,乘除的优先级别比加减高。
我们以键盘输入-3.12 + 2*3.5/4 + 8/2 - 3*6为例来说明算法。
式子中的2*3.5/4、8/2和3*6要先算,算完之后整个表达式将只有4个数据项,剩下的问题就很简单了。为此我们可以把整个表达式分成4部分(4块),块与块之间必定是由加减号连接的,每块中间只有乘除,没有加减。
实际上,没有必要先把每块的值都计算出来,在计算表达式的过程中,每遇到一块,可以先把这一块的结果算出来,然后与前面的计算结果进行加减运算,后面块的值可以在用到的时候再计算。
具体的计算方法是:
程序开始先定义一个变量result,并初始化为0。
从头开始处理每一块:提取一个数据,如果数据后面是乘(除),则表示该块计算未结束,继续提取第二个数据并与第一个数据进行乘(除)运算,然后再看后面的运算符,如果还是’*’或’/’,则继续提取数据并进行计算……直到遇到运算符‘+’、‘-’或空字符为止。
遇到运算符‘+’、‘-’或空字符,都表示该块的计算已经结束,可以将计算结果累加到result中了。
重复上面的方法,对每一块都作这样的处理,直到表达式结束。result就是最后的结果。
程序代码:
#include "stdio.h" #include "stdlib.h" float get_num(char **); int main() {char a[80]; int sign = 1; //对于表达式中的 ‘+’,sign = 1,对于‘-’,sign = -1 float result = 0; char *p; //用来指向要处理的字符 clrscr(); gets(a); p = a; if(*p == '-') { //表达式第一个字符若是‘-’ sign = -1; //存储符号 p++; } while(*p != 0) { //没有遇到空字符,则循环 float m; //m用来存储某块的计算结果 m = sign*get_num(&p); //get_num执行过程中,p会向后移动 while( *p == '*' || *p == '/' ){ //该循环求解某块的结果 if( *p == '*' ) { p++; m *= get_num(&p); } else { int div; p++; div = get_num(&p); if( div == 0 ) { printf("错误!除数为0!\n"); exit(1); } else m /= div; } } if(*p != '+' && *p != '-' && *p != 0){ printf("错误!遇到非法字符:%c\n", *p); exit(0); } result += m; //将上面计算出的某块的结果累加到result中 if(*p == '+'){ sign = 1; p++; } else if(*p == '-') { sign = -1; p++; } else //遇到空字符,退出循环 break; } printf("The result is %f\n", result); getch(); return 0; } float get_num(char **pp) //pp指向p {float x = 0, m = 10; if(**pp != '.' && (**pp <'0' || **pp >'9') ) { printf("错误!运算符后面不是一个有效的数据\n"); exit(1); } while(**pp >= '0' && **pp <= '9') { x = x*10 + **pp - 48; (*pp)++; } if(**pp == '.') { (*pp)++; while(**pp >= '0' && **pp <= '9') { x += (**pp - 48)/m; (*pp)++; m *= 10; } } return x; }
简单的计算器(二)
编程,用来计算用户输入的四则运算表达式的值,含加减乘除和括号。
实例解析:
本实例与上例的区别在于表达式中有括号。其实每个括号中的内容都可以看作是一个单独的表达式(也可能又带括号),考虑到这一点,我们可以把程序写为递归调用的方式,遇到括号则调用自己计算括号中表达式的值。相应的程序代码如下:
#include "stdio.h" #include "stdlib.h" float get_num(char **); float calculate(char **); int main() {char a[80]; float result; char *p; clrscr(); gets(a); p = a; result = calculate(&p); printf("%f\n", result); getch(); return 0; } float calculate(char **p) {float result = 0; int sign = 1; if(**p == '-') { sign = -1; (*p)++; } while(**p != 0 && **p != ')' ) { float m; if(**p == '(' ) { (*p)++; m = sign*calculate(p); //遇到括号调用自己计算括号中的值 } else m = sign*get_num(p); while(**p == '*' || **p == '/') { if(**p == '*') { (*p)++; if(**p == '(') { (*p)++; m *= calculate(p); //遇到括号调用自己计算括号中的值 } else m *= get_num(p); } else { int div; (*p)++; if(**p == '(') { (*p)++; div = calculate(p); //遇到括号调用自己计算括号中的值 } else div = get_num(p); if(div == 0) { printf("diveide by 0!\n"); exit(1); } else m /= div; } } if(**p != '+' && **p != '-' && **p != 0 && **p != ')'){ printf("illege character: %c\n",**p); exit(0); } result += m; if(**p == '+') { sign = 1; (*p)++; } else if(**p == '-' ) { sign = -1; (*p)++; } else if(**p == ')' ) { //遇到右括号,跳过并退出循环返回结果 (*p)++; break; } else break; } return result; } float get_num(char **p) {float n = 0, m = 10; if(**p != '.' && (**p <'0' || **p >'9')) { printf("get num error!\n"); exit(1); } while(**p >= '0' && **p <= '9') { n = n*10 + **p - 48; (*p)++; } if(**p == '.') { (*p)++; while(**p >= '0' && **p <= '9') { n += (**p - 48)/m; (*p)++; m *= 10; } } return n; }
本文出自 “成鹏致远” 博客,请务必保留此出处http://infohacker.blog.51cto.com/6751239/1171349