Problem A: 位操作
Description
假设你工作在一个32位的机器上,你需要将某一个外设寄存器的第X位设置成0(最低位为第0位,最高位为第31位),将第Y位开始的连续三位设置成110(从高位到低位的顺序),而其他位保持不变。对给定的寄存器值R,及X,Y,编程计算更改后的寄存器值R。
Input
仅一行,包括R,X,Y,以逗号","分隔,R为16进制表示的32位整数,X,Y在0-31之间且Y>=3,(Y-X)的绝对值>=3,保证两次置位不会重合
Output
更改后的寄存器值R(16进制输出)
Sample Input
12345678,0,3
Sample Output
1234567c
解题思路:
很简单的位操作,但是需要注意的是Y那里是 110,不能直接或上110,而是先两次SET,在CLR。dave就在这里浪费了有1个小时,惭愧
code
#include <stdio.h> #define CLR(r, x) r &= ~(1UL << x) #define SET(r, y) r |= (1UL << y) int main() { int r, x, y; scanf("%x,%d,%d", &r, &x, &y); CLR(r,x); SET(r,y); SET(r,y-1); CLR(r,y-2); printf("%x", r); return 0; }
———————————————————————————————————————————————————————————————————————————————————————————————————————————————————— Problem B: 破译密码
Description
据说最早的密码来自于罗马的凯撒大帝。消息加密的办法是:对消息原文中的每个字母,分别用该字母之后的第5个字母替换(例如:消息原文中的每个字母A都分别替换成字母F)。而你要获得消息原文,也就是要将这个过程反过来。
密码字母:A B C D E F G H I J K L M N O P Q R S T U V W X Y Z M
原文字母:V W X Y Z A B C D E F G H I J K L M N O P Q R S T U
注意:只有字母会发生替换,其他非字母的字符不变,并且消息原文的所有字母都是大写的。
Input
最多不超过100个数据集组成,每个数据集之间不会有空行,每个数据集由3部分组成:
- 起始行:START
- 密码消息:由1到200个字符组成一行,表示凯撒发出的一条消息.
- 结束行:END
在最后一个数据集之后,是另一行:ENDOFINPUT
Output
每个数据集对应一行,是凯撒的原始消息。
Sample Input
START
NS BFW, JAJSYX TK NRUTWYFSHJ FWJ YMJ WJXZQY TK YWNANFQ HFZXJX
END
START
N BTZQI WFYMJW GJ KNWXY NS F QNYYQJ NGJWNFS ANQQFLJ YMFS XJHTSI NS WTRJ
END
START
IFSLJW PSTBX KZQQ BJQQ YMFY HFJXFW NX RTWJ IFSLJWTZX YMFS MJ
END
ENDOFINPUT
Sample Output
IN WAR, EVENTS OF IMPORTANCE ARE THE RESULT OF TRIVIAL CAUSES
I WOULD RATHER BE FIRST IN A LITTLE IBERIAN VILLAGE THAN SECOND IN ROME
DANGER KNOWS FULL WELL THAT CAESAR IS MORE DANGEROUS THAN HE
解题思路
凯撒编码,判断字符是否是字母,并循环-5即可,记得要循环哦,非常简单的题目哦
code
#include <stdio.h> #include <string.h> #include <ctype.h> #define N 202 char str[N]={0}; int main() { char *p; gets(str); while( strcmp(str, "ENDOFINPUT") != 0 ) { if ( (strcmp(str, "START") !=0) &&(strcmp(str, "END") != 0) ) { for(p=str; *p !='/0'; p++) { if( isupper(*p) ) *p += *p-5 <'A' ? 26-5: -5; } puts(str); } gets(str); } return 0; }
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
Problem C: 小孩报数问题
Description
有N个小孩围成一圈,给他们从1开始依次编号,现指定从第W个开始报数,报到第S个时,该小孩出列,然后从下一个小孩开始报数,仍是报到S个出列,如此重复下去,直到所有的小孩都出列(总人数不足S个时将循环报数),求小孩出列的顺序。
Input
第一行输入小孩的人数N(N<=64)
接下来每行输入一个小孩的名字(人名不超过15个字符)
最后一行输入W,S (W < N),用逗号","间隔
Output
按人名输出小孩按顺序出列的顺序,每行输出一个人名
Sample Input
5
Xiaoming
Xiaohua
Xiaowang
Zhangsan
Lisi
2,3
Sample Output
Zhangsan
Xiaohua
Xiaoming
Xiaowang
Lisi
解题思路
约瑟夫环的问题,简单题目,但是dave一开始把题目看的不细心,以为是倒序输出,就花了很长时间用链表解决,结果超时(就算不超时也肯定错误),后来一看原来是正序,又用数组重写。 需要注意的就是第一次数人从当前s点就算第一个,以后数人下个人才算第一个,用一点点小技巧把第一数人的起始点向前退1个就ok了。这里也浪费了1个小时
code
#include <stdio.h> #define PEOPLE_NUM 65 #define NAME_LEN 16 struct people { char name[NAME_LEN]; char flag; }; void input(); void calc(); struct people children[PEOPLE_NUM]; int n,w,s; int main() { input(); calc(); return 0; } void input() { int i; scanf("%d/n", &n); for(i=1; i<=n ;i++) { gets(children[i].name); children[i].flag=0; } scanf("%d,%d", &w, &s); } void calc() { //struct people *p; int i ,out ,pos ; for(out=0,pos=w-1; out<n ; out++) { for(i=0; i<s;) { pos= (pos == n ? 1:pos+1); if(children[pos].flag == 0) i++; } puts(children[pos].name); children[pos].flag = 1; } }
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
Problem D:
时间日期格式转换
Description
世 界各地有多种格式来表示日期和时间。对于日期的常用格式,在中国常采用格式的是“年年年年/月月/日日”或写为英语缩略表示的”yyyy/mm/dd”, 此次编程大赛的启动日期“2009/11/07”就是符合这种格式的一个日期,而北美所用的日期格式则为“月月/日日/年年年年”或”mm/dd /yyyy”,如将“2009/11/07”改成这种格式,对应的则是”11/07/2009”。对于时间的格式,则常有12小时制和24小时制的表示方 法,24小时制用0-24来表示一天中的24小时,而12小时制只采用1-12表示小时,再加上am/pm来表示上午或下午,比如”17:30:00”是 采用24小时制来表示时间,而对应的12小时制的表示方法是”05:30:00pm”。注意12:00:00pm表示中午12点,而12:00:00am 表示凌晨12点。
对于给定的采用”yyyy/mm/dd”加24小时制(用短横线”-”连接)来表示日期和时间的字符串,请编程实现将其转换成”mm/dd/yyyy”加12小时制格式的字符串。
Input
第一行为一个整数T(T<=10),代表总共需要转换的时间日期字符串的数目。
接下来的总共T行,每行都是一个需要转换的时间日期字符串。
Output
分行输出转换之后的结果
Sample Input
2 2009/11/07-12:12:12
1970/01/01-00:01:01
Sample Output
11/07/2009-12:12:12pm
01/01/1970-12:01:01am
Hint
注意中午和凌晨时间的特殊表示
解题思路
算法简单但是功能稍微复杂的题目。
日期的变更可以利用向量循环移位的思路来解决,但是稍稍不同,因为yyyy/mm/dd循 环移位只能变成mm/ddyyyy/,所以要变通下。先y1y2y3y4翻转变成y4y3y2y1,在m1m2/d1d2翻转变成d2d1/m2m1,然 后整体y4y3y2y1/d2d1/m2m1翻转变成m1/m2/d1/2/y1/y2/y3/y4 解决。
日期的变更就只能用if语句按照小时来判断了,注意12小时制没有0点,最少1点,最多12点。我就是这个没有理解WA了两次,后来用手机测试了下,理解了变换步骤,终于解决。注意24小时制中的0点xx和12点xx变换都为12点am/pm.
code
#include <stdio.h> #include <string.h> #define T 10 #define DATE_LEN 10 #define TIME_LEN 10 #define SUM_LEN 22 static void reverse(char a[], int n); void change_date(char date[]); void change_time(char time[]); int t; char datetime[T][SUM_LEN]; int main() { int i; scanf("%d/n", &t); //scanf for(i=0; i<t; i++) { gets(datetime[i]); } //change date & time for(i=0; i<t; i++) { //date change_date(datetime[i]); //time change_time(datetime[i]+DATE_LEN+1); } //output for(i=0; i<t; i++) { puts(datetime[i]); } return 0; } static void reverse(char a[], int n) { int i; char t; for(i=0,n--; i<n-i; i++) { t = a[i]; a[i] = a[n-i]; a[n-i] = t; } } void change_date(char date[]) { reverse(date, 4); reverse(date+5, 5); reverse(date, DATE_LEN); } void change_time(char time[]) { if ( (time[0]>='1') && (time[1]>='2') ) { if( (time[0]!='1') || (time[1]!='2') ) { time[0] -= 1; time[1] -= 2; } strcat(time, "pm"); } else { if( (time[0]=='0') && (time[1]=='0') ) { time[0] += 1; time[1] += 2; } strcat(time, "am"); } }
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
Problem E:
字母旋转游戏
Description
给定两个整数M,N,生成一个M*N的矩阵,矩阵中元素取值为A至Z的26个字母中的一个,A在左上角,其余各数按顺时针方向旋转前进,依次递增放置,当超过26时又从A开始填充。例如,当M=5,N=8时,矩阵中的内容如下: A B C D E F G H V W X Y Z A B I U J K L M N C J T I H G F E D K S R Q P O N M L
Input
M为行数,N为列数,其中M,N都为大于0的整数。
Output
分行输出相应的结果
Sample Input
4 9
Sample Output
A B C D E F G H I
V W X Y Z A B C J
U J I H G F E D K
T S R Q P O N M L
解题思路
稍微有点难度的题目,最重要的是矩阵四个边的边界值以及填充完成条件。在我的代码里,设置了四个变量u,d,l,r,分别代表上下左右的边界[u,d),[l,r),如果有两个边界重合就说明已经填充完毕了。当然你也可以用填充计数器来判断,如果等于行*列数,也是填满了。建立正确而又简洁的模型很重要。
code
#include <stdio.h> #include <stdlib.h> void calc(char *ma, int m, int n); void output(char *ma, int m, int n); int main() { int m=4, n=9; char *matrix; scanf("%d %d", &m, &n); matrix = malloc(m*n*sizeof(char)); if (matrix == NULL) return -1; calc(matrix, m, n); output(matrix, m, n); return 0; } void output(char *ma, int m, int n) { int i,j; for(i=0; i<m; i++) { for(j=0; j<n; j++) { printf("%4c", ma[n*i+j]); } puts(""); } } void calc(char *ma, int m, int n) { int i=0, j=-1; int u,d,l,r,sum; sum=0; u=0; d=m; l=0; r=n; while(1) { if (l==r) break; for(j++; j<r; j++) { ma[n*i+j] = (sum++)%26 +'A'; } u++; j--; if (u==d) break; for(i++; i<d; i++) { ma[n*i+j] = (sum++)%26 +'A'; } r--; i--; if (l==r) break; for(j--; j>=l; j--) { ma[n*i+j] = (sum++)%26 +'A'; } d--; j++; if (u==d) break; for(i--; i>=u; i--) { ma[n*i+j] = (sum++)%26 +'A'; } l++; i++; } }
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
总结:
这5个小题,一共花了dave,6个小时的时间,看来自从高中OI以后,能力退化了很多。按照OI的水平这些题目都是2个小时完成的。更不说ACM了。。。
1、建立好模型再编码,与其花很多精力调试,不如一开始就把问题考虑全,这个能力跟做题经验有关。
2、读懂读清题目,第一题就是这样子,dave都不好意说了,还犯这样的低级错误。
3、调试的时候根据故障快速定位,不要漫无目的的。思考,很重要。