- 数组只有在放在main函数外面时,才可以开得很大;放在main函数内时,数组稍大就会异常退出。
- 如果要从数组a复制k个元素到数组b,可以这样做:
memcpy(b,a,sizeof(int) * k)
- 如果数组a和b都是浮点型的,复制时要写成
memcpy(b,a,sizeof(double) * k)
- 使用memcpy函数要包含头文件string.h
- 如果需要把数组a全部复制到数组b中,可以写得简单一些:
memcpy(b,a,sizeof(a))
- 在很多情况下,最好是在做一件事之前检查是不是可以做,而不要做完再后悔。因为“悔棋”往往比较麻烦。
scanf("%s", s)
会读入一个不含空格、TAB和回车符的字符串,存入字符数组s。- 如果是字符串数组chars[maxn] [maxl],可以用“scanf(“%s”, s[i])”读取第i个字符串。
scanf("%s", s)
遇到空白字符会停下来。- %5d,它表示按照5位数打印,不足5位在前面补空格。
strchr
的作用是在一个字符串中查找单个字符。- printf输出到屏幕,fprintf输出到文件,而sprintf输出到字符串。sprintf应该保证写入的字符串有足够的空间。
- C语言的字符串是以空字符“\0”结尾的。
- 函数
strlen(s)
的作用是获取字符串s的实际长度。字符串中的各个字符依次是s[0], s[1],…, s[strlen(s)-1],而s[strlen(s)]正是结束标记“\0”。 - 由于字符串的本质是数组,它也不是“一等公民”,只能用
strcpy(a, b)
,strcmp(a, b)
,strcat(a, b)
来执行“赋值”、“比较”和“连接”操作,而不能用“=”、“==”、“<=”、“+”等运算符。上述函数都在string.h中声明。 - “\n”表示换行,“\”表示反斜杠,“\””表示引号,“\0”表示空字符。
- 语句
printf("%d %o %x\n", a)
将把整数a分别按照十进制、八进制和十六进制输出。
程序3-2 开灯问题
有n盏灯,编号为1~n。第1个人把所有灯打开,第2个人按下所有编号为2的倍数的开关(这些灯将被关掉),第3个人按下所有编号为3的倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭),依此类推。一共有k个人,问最后有哪些灯开着?输入n和k,输出开着的灯的编号。k≤n≤1000。
样例输入:
7 3
样例输出:
1 5 6 7
#include<stdio.h>
#include<string.h>
#define maxn 1010
int a[maxn];
int main() {
int n, k;
memset(a, 0, sizeof(a));
while(scanf("%d%d", &n, &k)!=EOF) {
int first = 1;
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;
}
- memset(a,0,sizeof(a))”的作用是把数组a清零,它也在string.h中定义。虽然也能用for循环完成相同的任务,但是用memset又方便又快捷。
- 为了避免输出
多余空格,设置了一个标志变量first,可以表示当前要输出的变量是否为第一个。第一个变量前不应有空格,但其他变量都有。
程序3-3 蛇形填数
在n×n方阵里填入1,2,…,n×n,要求填成蛇形。n≤8。
#include<stdio.h>
#include<string.h>
#define maxn 20
int a[maxn][maxn];
int main() {
int n;
while(scanf("%d", &n) != EOF) {
int x, y, tot = 0;
memset(a, 0, sizeof(a));
tot = a[x=0][y=n-1] = 1;
while(tot < n*n) {
while(x+1<n && !a[x+1][y]) {
a[++x][y] = ++tot;
// printf("a[%d][%d]:%d\n",x,y,a[x][y]);
}
while(y-1>=0 && !a[x][y-1]) {
a[x][--y] = ++tot;
// printf("a[%d][%d]:%d\n",x,y,a[x][y]);
}
while(x-1>=0 && !a[x-1][y]) {
a[--x][y] = ++tot;
// printf("a[%d][%d]:%d\n",x,y,a[x][y]);
}
while(y+1<n && !a[x][y+1]) {
a[x][++y] = ++tot;
// printf("a[%d][%d]:%d\n",x,y,a[x][y]);
}
}
for(x = 0; x < n; x++) {
for(y=0; y < n; y++) {
printf("%3d", a[x][y]);
}
printf("\n");
}
}
return 0;
}
程序3-4 竖式问题
找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合。输入数字集合(相邻数字之间没有空格),输出所有竖式。每个竖式前应有编号,之后应有一个空行。最后输出解的总数。
#include<stdio.h>
#include<string.h>
int main() {
int count = 0;
char s[20], buf[99];
scanf("%s", s);
for(int abc = 111; abc <=999; abc ++) {
for(int de = 11; de <=99; de++) {
int x = abc * (de%10);
int y = abc * (de/10);
int z = abc * de;
sprintf(buf, "%d%d%d%d%d",abc,de,x,y,z);
int ok = 1;
for(int i=0; i<strlen(buf); i++) {
if(strchr(s, buf[i]) == NULL) {
ok = 0;
}
}
if(ok) {
printf("<%d>\n", ++count);
printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d\n\n", abc, de, x, y, z);
}
}
}
printf("The number of solutions = %d\n", count);
return 0;
}
例题3-1 TeX中的引号(Tex Quotes, UVa 272)
在TeX中,左双引号是“``”
,右双引号是“''”
。输入一篇包含双引号的文章,你的任务是
把它转换成TeX的格式。
样例输入:
"To be or not to be," quoth the Bard, "that
is the question".
样例输出:
``To be or not to be,'' quoth the Bard, ``that
is the question''.
之前学习了使用“scanf("%s")”输入字符串,但却不能在本题中使用它,因为它碰到空格
或者TAB就会停下来。虽然下次调用时会输入下一个字符串,可是不知道两次输入的字符串
中间有多少个空格、TAB甚至换行符。可以用下述两种方法解决这个问题:
第一种方法是使用“fgetc(fin)”,它读取一个打开的文件fin,读取一个字符,然后返回一
个int值。为什么返回的是int而不是char呢?因为如果文件结束,fgetc将返回一个特殊标记
EOF,它并不是一个char。如果把fgetc(fin)的返回值强制转换为char,将无法把特殊的EOF和
普通字符区分开。如果要从标准输入读取一个字符,可以用getchar,它等价于fgetc(stdin)。
使用fgetc(fin)可以从打开的文件fin中读取一个字符。一般情况下应当在检
查它不是EOF后再将其转换成char值。从标准输入读取一个字符可以用getchar,它等价于
fgetc(stdin)。
fgetc和getchar将读取“下一个字符”,因此需要知道在各种情况下,“下一个字符”是哪
个。如果用“scanf("%d", &n)”读取整数n,则要是在输入123后多加了一个空格,用getchar读
取的将是这个空格;如果在“123”之后紧跟着换行,则读取到的将是回车符“\n”。
这里有个潜在的陷阱:不同操作系统的回车换行符是不一致的。Windows是“\r”和“\n”两
个字符,Linux是“\n”,而MacOS是“\r”。如果在Windows下读取Windows文件,fgetc和getchar
会把“\r""吃掉”,只剩下“\n”;但如果要在Linux下读取同样一个文件,它们会忠实地先读
取“\r”,然后才是“\n”。如果编程时不注意,所写程序可能会在某个操作系统上是完美的,但
在另一个操作系统上就错得一塌糊涂。当然,比赛的组织方应该避免在Linux下使用Windows
格式的文件,但正如前面所强调过的:选手也应该把自己的程序写得更鲁棒,即容错性更
好。
第二种方法是使用“fgets(buf, maxn, fin)”读取完整的一行,其中buf的声明为char
buf[maxn]。这个函数读取不超过maxn-1个字符,然后在末尾添上结束符“\0”,因此不会出现
越界的情况。之所以说可以用这个函数读取完整的一行,是因为一旦读到回车符“\n”,读取
工作将会停止,而这个“\n”也会是buf字符串中最后一个有效字符(再往后就是字符串结束
符“\0”了)。只有在一种情况下,buf不会以“\n”结尾:读到文件结束符,并且文件的最后一
个不是以“\n”结尾。尽管比赛的组织方应避免这样的情况(和输出文件一样,保证输入文件
的每行均以回车符结尾),但正如刚才所说,选手应该把自己的程序写得更鲁棒。
"fgets(buf, maxn, fin)"将读取完整的一行放在字符数组buf中。应当保证
buf足够存放下文件的一行内容。除了在文件结束前没有遇到“\n”这种特殊情况外,buf总是
以“\n”结尾。当一个字符都没有读到时,fgets返回NULL。
和fgetc一样,fgets也有一个"标准输入版"gets。遗憾的是,gets和它的"兄弟"fgets差别比
较大:其用法是gets(s),没有指明读取的最大字符数。这里就出现了一个潜在的问题:gets
将不停地往s中存储内容,而不管是否存储得下!难道gets函数不去管s的可用空间有多少
吗?确实如此。
C语言并不禁止程序读写"非法内存"。例如,声明的是char s[100],完全可
以赋值s[10000] = 'a'(甚至-Wall也不会警告),但后果自负。
正是因为如此,gets已经被废除了,但为了向后兼容,仍然可以使用它。从长远考虑,
最好不要使用此函数。事实上,在C11标准里,gets函数已被正式删除。
C语言中的gets(s)存在缓冲区溢出漏洞,不推荐使用。在C11标准里,该函
数已被正式删除。
例题3-2 WERTYU(WERTYU, UVa10082)
把手放在键盘上时,稍不注意就会往右错一位。这样,输入Q会变成输入W,输入J会变成输入K等。输入一个错位后敲出的字符串(所有字母均大写),输出打字员本来想打出的句子。输入保证合法,即一定是错位之后的字符串。例如输入中不会出现大写字母A。
#include<stdio.h>
char s[] = "`1234567890-=QWERTYUIOP[]\\ASDFGHJKL;'ZXCVBNM,./";
int main() {
int i, c;
while((c = getchar()) != EOF) {
for (i=1; s[i] && s[i]!=c; i++);
if (s[i]) {
printf("%c", s[i-1]);
// putchar(s[i-1]);
}
else {
// putchar(c);
printf("%c", c);
}
}
return 0;
}
例题3-3 回文词(Palindromes, UVa401)
输入一个字符串,判断它是否为回文串以及镜像串。输入字符串保证不含数字0。所谓回文串,就是反转以后和原串相同,如abba和madam。所有镜像串,就是左右镜像之后和原串相同,如2S和3AIAE。注意,并不是每个字符在镜像之后都能得到一个合法字符。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
const char* rev = "A 3 HIL JM O 2TUVWXY51SE Z 8 ";
const char* msg[] = {"not a palindrome", "a regular palindrome", "a mirrored string", "a mirrored palindrome"};
char r(char ch) {
if(isalpha(ch)) return rev[ch - 'A'];
return rev[ch - '0' + 25];
}
int main() {
char s[30];
while(scanf("%s", s) == 1) {
int len = strlen(s);
int p = 1, m = 1;
for(int i = 0; i < (len+1)/2; i++) {
if(s[i] != s[len-1-i]) {
p = 0; //不是回文串
}
if(r(s[i]) != s[len-1-i]) {
m = 0; //不是镜像串
}
}
printf("%s -- is %s.\n\n", s, msg[m*2+p]);
}
return 0;
}
本题用isalpha来判断字符是否为字母,类似的还有isdigit、isprint等,在ctype.h中定义。
由于ASCII码表中大写字母、小写字母和数字都是连续的,如果ch是大写字母,则ch-'A'就是
它在字母表中的序号(A的序号是0,B的序号是1,依此类推);类似地,如果ch是数字,
则ch-'0'就是这个数字的数值本身(例如'5'-'0'=5)。
头文件ctype.h中定义的isalpha、isdigit、isprint等工具可以用来判断字符
的属性,而toupper、tolower等工具可以用来转换大小写。如果ch是大写字母,则ch-'A'就是
它在字母表中的序号(A的序号是0,B的序号是1,依此类推);类似地,如果ch是数字,
则ch-'0'就是这个数字的数值本身。
例题3-4 猜数字游戏的提示(Master-Mind Hints, UVa 340)
实现一个经典”猜数字”游戏。给定答案序列和用户猜的序列,统计有多少数字位置正确(A),有多少数字在两个序列都出现过但位置不对(B)。
输入包含多组数据。每组输入第一行为序列长度n,第二行是答案序列,接下来是若干猜测序列。猜测序列全0时该组数据结束。n=0时输入结束。
#include<stdio.h>
#define maxn 1010
int main() {
int n, a[maxn], b[maxn];
int kase = 0;
while(scanf("%d", &n) == 1 && n) {
printf("Game %d:\n", ++kase);
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
for(;;) {
int A = 0, B = 0;
for(int i=0;i<n;i++) {
scanf("%d", &b[i]);
if(a[i] == b[i]) {
A++;
}
}
if(b[0] == 0) break;
for(int d = 1; d <= 9; d++) {
int c1 = 0, c2 = 0;
for(int i=0;i<n;i++) {
if(a[i] == d) {
c1++;
}
if(b[i] == d) {
c2++;
}
}
if(c1 < c2) {
B += c1;
} else {
B += c2;
}
}
printf(" (%d,%d)\n",A,B-A);
}
}
return 0;
}
例题3-5 生成元(Digit Generator, ACM/ICPC Seoul 2005, UVa1583)
如果x加上x的各个数字之和得到y,就说x是y的生成元。给出n(1≤n≤100000),求最小
生成元。无解输出0。例如,n=216,121,2005时的解分别为198,0,1979。
#include<stdio.h>
#include<string.h>
#define maxn 100005
int ans[maxn];
int main() {
int T, n;
memset(ans, 0, sizeof(ans));
for(int m = 1; m < maxn; m++) {
int x = m, y = m;
while(x > 0) {
y += x %10;
x /= 10;
}
if(ans[y] == 0 || m < ans[y]) {
ans[y] = m;
}
}
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
printf("%d\n", ans[n]);
}
return 0;
}
例题3-6 环状序列(Circular Sequence, ACM/ICPC Seoul 2004, UVa1584)
长度为n的环状串有n种表示法,分别为从某个位置开始顺时针得到。例如,图3-4的环状串有10种表示:CGAGTCAGCT,GAGTCAGCTC,AGTCAGCTCG等。在这些表示法中,字典序最小的称为”最小表示”。输入一个长度为n(n≤100)的环状DNA串(只包含A、C、G、T这4种字符)的一种表示法,你的任务是输出该环状串的最小表示。例如,CTCC的最小表示是
CCCT,CGAGTCAGCT的最小表示为AGCTCGAGTC。
本题出现了一个新概念:字典序。所谓字典序,就是字符串在字典中的顺序。一般地,
对于两个字符串,从第一个字符开始比较,当某一个位置的字符不同时,该位置字符较小的
串,字典序较小(例如,abc比bcd小);如果其中一个字符串已经没有更多字符,但另一个
字符串还没结束,则较短的字符串的字典序较小(例如,hi比history小)。字典序的概念可
以推广到任意序列,例如,序列1, 2, 4, 7比1, 2, 5小。
#include<stdio.h>
#include<string.h>
#define maxn 105
int less(const char* s, int p, int q) {
int n = strlen(s);
for(int i=0;i<n;i++) {
if(s[(p+i)%n] != s[(q+i)%n]) {
return s[(p+i)%n] < s[(q+i)%n];
}
}
return 0;
}
int main() {
int T;
char s[maxn];
scanf("%d", &T);
while(T--) {
scanf("%s",s);
int ans = 0;
int n = strlen(s);
for(int i = 1; i < n; i++) {
if(less(s, i , ans)) {
ans = i;
}
}
for(int i = 0; i < n; i++) {
putchar(s[(i+ans)%n]);
}
putchar('\n');
}
return 0;
}
二进制转换为十进制
- 类似于123=((1*10)+2)*10+3,二进制转换为十进制也可以这样一次添加一位,每次乘以2:101=((1*2+0)*2+1=5。
- 在C语言中,“乘以2”也可以写成“<<1”,意思是“左移一位”。类似地,左移4位就是乘以 。
- 在二进制中,8位最大整数就是8个1,即2 8 -1,用C语言写出来就是(1<<8)-1。注意括号是必需的,因为“<<”运算符的优先级没有减法高。
- 补码表示法。计算机中的二进制是没有符号的。尽管123的二进制值是1111011,-123在计算机内并不表示为-1111011——这个“负号”也需要用二进制位来表示。
- 语句“printf(“%u\n”,-1)”的输出是4294967295 (4) 。把-1换成-2、-3、-4……后,很容易总结出一个规律:-n的内部表示是2 32 -n。这就是著名的“补码表示法”(Complement Representation)。
- 为什么计算机要用这样一个奇怪的表示方法呢?前面提到的“符号位+绝对值”的方法哪里不好了?答案是:运算不方便。试想,要计算1 + (-1)的值(为了简单起见,假设两个数都是带符号8位整数)。如果用“符号位+绝对值”法,将要计算00000001+10000001,而答案应该是00000000。似乎想不到什么简单的方法进行这个“加法”。但如果采用补码表示,计算的是00000001+11111111,只需要直接相加,并丢掉最高位的进位即可。“符号位+绝对值”还有一个好玩的bug:存在两种不同的0:一个是00000000(正0),一个是10000000(负0)。这个问题在补码表示法中不会出现。
习题3-1 得分(Score, ACM/ICPC Seoul 2005, UVa1585)
给出一个由O和X组成的串(长度为1~80),统计得分。每个O的得分为目前连续出现
的O的个数,X的得分为0。例如,OOXXOXXOOO的得分为1+2+0+0+1+0+0+1+2+3。
Sample Input
5
OOXXOXXOOO
OOXXOOXXOO
OXOXOXOXOXOXOX
OOOOOOOOOO
OOOOXOOOOXOOOOX
Sample Output
10
9
7
55
30
#include<stdio.h>
#include<string.h>
#define maxn 80 + 10
char s[maxn];
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%s", s);
int sum = 0;
int tot = 1;
for(int i = 0; i < strlen(s); i++) {
if(s[i] == 'O') {
sum = sum + tot++;
} else if(s[i] == 'X') {
tot = 1;
}
}
printf("%d\n", sum);
}
return 0;
}
习题3-2 分子量(Molar Mass, ACM/ICPC Seoul 2007, UVa1586)
给出一种物质的分子式(不带括号),求分子量。本题中的分子式只包含4种原子,分别为C, H, O, N,原子量分别为12.01, 1.008, 16.00, 14.01(单位:g/mol)。例如,C6H5OH的分子量为94.108g/mol。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define C 12.01
#define H 1.008
#define O 16.00
#define N 14.01
char s[1000];
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%s", s);
int n = strlen(s);
int i;
double sum = 0;
for(i = 0; i<n;) {
int ans=1;
if(isdigit(s[i+1])) {
if(isdigit(s[i+2])) {
ans = (s[i+1]-'0')*10+(s[i+2]-'0');
} else {
ans = s[i+1]-'0';
}
}
switch(s[i]) {
case 'C' : sum += C*ans; break;
case 'H' : sum += H*ans; break;
case 'O' : sum += O*ans; break;
case 'N' : sum += N*ans; break;
default : break;
}
i += (ans==1?1:(ans>9?3:2));
}
printf("%.3f\n",sum);
}
return 0;
}
习题3-3 数数字(Digit Counting , ACM/ICPC Danang 2007, UVa1225)
把前n(n≤10000)个整数顺次写在一起:123456789101112…数一数0~9各出现多少次(输出10个整数,分别是0,1,…,9出现的次数)。
#include<stdio.h>
#include<string.h>
int main() {
int T;
scanf("%d",&T);
while(T--) {
int n;
scanf("%d", &n);
int a[10];
memset(a, 0, sizeof(a));
for(int i=1;i<=n;i++) {
int temp = i;
while(temp) {
a[temp%10]++;
temp/=10;
}
}
for(int i=0;i<9;i++) {
printf("%d ",a[i]);
}
printf("%d\n",a[9]);
}
return 0;
}
习题3-4 周期串(Periodic Strings, UVa455)
如果一个字符串可以由某个长度为k的字符串重复多次得到,则称该串以k为周期。例如,abcabcabcabc以3为周期(注意,它也以6和12为周期)。
输入一个长度不超过80的字符串,输出其最小周期。
#include<stdio.h>
#include<string.h>
#define maxn 80 + 10
char s[maxn];
int main() {
int T, i, j;
scanf("%d",&T);
while(T--) {
scanf("%s", s);
int n = strlen(s);
for(i=1; i<=n; i++) { // 周期从1到n
if(n%i==0) { // 周期要可以均分字符串
for(j=i; j<n; j++) {
if(s[j] != s[j%i]) {
break;
}
}
if(j==n) {
printf("%d\n", i);
break;
}
}
}
if(T) {
printf("\n");
}
}
return 0;
}
习题3-5 谜题(Puzzle, ACM/ICPC World Finals 1993, UVa227)
有一个5*5的网格,其中恰好有一个格子是空的,其他格子各有一个字母。一共有4种指令:A, B, L, R,分别表示把空格上、下、左、右的相邻字母移到空格中。输入初始网格和指令序列(以数字0结束),输出指令执行完毕后的网格。如果有非法指令,应输出“Thispuzzle has no final configuration.”
被这道题的输入折磨的很烦
#include<stdio.h>
#include<string.h>
#define maxn 8
char s[maxn][maxn];
char in[1000];
int main() {
bool line = false;
int T = 0;
while(true) {
int x=0,y=0;
for(int i=0; i<5;i++) {
gets(s[i]);
if (strcmp(s[i], "Z") == 0) {
return 0;
}
}
for(int i=0; i<5;i++) {
for(int j=0; j<5; j++) {
if(s[i][j] == ' ' || s[i][j]==0) {
x = i;
y = j;
i = j = 5;
}
}
}
int flag = 0;
do {
gets(in);
for(int k = 0; k<strlen(in); k++) {
if(in[k] == '0') {
break;
}
else if(in[k] == 'A') {
if(x-1 < 0 ) {
flag = 1;
break;
}
s[x][y] = s[x-1][y];
s[--x][y] = ' ';
}
else if(in[k] == 'B') {
if(x+1 > 4 ) {
flag = 1;
break;
}
s[x][y] = s[x+1][y];
s[++x][y] = ' ';
}
else if(in[k] == 'L') {
if(y-1 < 0 ) {
flag = 1;
break;
}
s[x][y] = s[x][y-1];
s[x][--y] = ' ';
}
else if(in[k] == 'R') {
if(y+1 > 4 ) {
flag = 1;
break;
}
s[x][y] = s[x][y+1];
s[x][++y] = ' ';
}
else if(in[k] == '\n') {
continue;
}
else{
flag = 1;
break;
}
}
} while(in[strlen(in)-1]!='0');
if (line)
printf ("\n");
line = true;
printf("Puzzle #%d:\n", ++T);
if(flag) {
printf("This puzzle has no final configuration.\n");
} else {
for(int i=0; i<5;i++) {
for(int j=0; j<5; j++) {
if(j!=0) {
printf(" ");
}
printf("%c",s[i][j]);
}
printf("\n");
}
}
}
return 0;
}
#include <stdio.h>
#include <string.h>
int main () {
int cases = 0;
bool line = false;
char initial[5][7];
while(gets(initial[0])) {
if(strcmp(initial[0], "Z") == 0) {
return 0;
}
gets(initial[1]);
gets(initial[2]);
gets(initial[3]);
gets(initial[4]);
int blank_x;
int blank_y;
for(int i=0; i<5; i++) {
for(int j=0; j<5; j++) {
if(initial[i][j] == ' ') {
blank_x = i;
blank_y = j;
i = j = 5;
}
}
}
char command[1000];
bool valid = true;
bool exit_koro = false;
while(!exit_koro && gets(command)) {
for(int i=0; command[i]!=0; i++) {
if(command[i] == '0' || !valid) {
exit_koro = true;
break;
}
switch(command[i]) {
case 'A' :
if(blank_x == 0) {
valid = false;
} else {
initial[blank_x][blank_y] = initial[blank_x-1][blank_y];
initial[blank_x-1][blank_y] = ' ';
blank_x--;
}
break;
case 'B' :
if(blank_x == 4) {
valid = false;
} else {
initial[blank_x][blank_y] = initial[blank_x+1][blank_y];
initial[blank_x+1][blank_y] = ' ';
blank_x++;
}
break;
case 'L' :
if(blank_y == 0) {
valid = false;
} else {
initial[blank_x][blank_y] = initial[blank_x][blank_y - 1];
initial[blank_x][blank_y - 1] = ' ';
blank_y--;
}
break;
case 'R' :
if(blank_y == 4) {
valid = false;
} else {
initial[blank_x][blank_y] = initial[blank_x][blank_y + 1];
initial[blank_x][blank_y + 1] = ' ';
blank_y++;
}
break;
}
}
}
if(line) {
printf("\n");
}
line = true;
printf ("Puzzle #%d:\n", ++cases);
if(valid) {
for(int i=0; i<5; i++) {
printf("%c %c %c %c %c\n", initial[i][0],initial[i][1],initial[i][2],initial[i][3],initial[i][4]);
}
} else {
printf ("This puzzle has no final configuration.\n");
}
}
return 0;
}
习题3-6 纵横字谜的答案(Crossword Answers, ACM/ICPC World Finals 1994,
UVa232)
输入一个r 行c 列(1
#include<stdio.h>
#include<string.h>
#define maxn 12
int main() {
int r,c;
bool line = false;
int ans = 1;
while(scanf("%d",&r) != EOF) {
if(r == 0) break;
scanf("%d", &c);
char s[maxn][maxn];
int num[maxn][maxn];
int tot=1;
for(int i=0; i<r;i++) {
scanf("%s",s[i]);
}
memset(num, 0, sizeof(num));
for(int i=0; i<r; i++) {
for(int j=0; j<c; j++) {
if(s[i][j]=='*') {
num[i][j]=-1;
// printf("%c\n",s[i][j]);
}else if((j-1)<0 || (i-1)<0 || s[i][j-1]=='*' || s[i-1][j]=='*') {
num[i][j]=tot++;
}
}
}
// for(int i=0; i<r; i++) {
// for(int j=0; j<c; j++) {
// printf("%d ",num[i][j]);
// }
// printf("\n");
// }
if(line) {
printf("\n");
}
line = true;
printf("puzzle #%d:\n", ans++);
printf("Across\n");
for(int i=0; i<r; i++) {
for(int j=0; j<c; j++) {
if((j-1<0 || num[i][j-1]==-1) && num[i][j]>0){
printf("%3d.",num[i][j]);
for(int k=j;k<c;k++){
printf("%c",s[i][k]);
if(num[i][k+1]==-1 || k+1==c) {
printf("\n");
j=k;
break;
}
}
}
}
}
printf("Down\n");
for(int i=0; i<r; i++) {
for(int j=0; j<c; j++) {
if((i-1<0 || num[i-1][j]==-1) && num[i][j]>0) {
printf("%3d.",num[i][j]);
for(int k=i;k<r;k++) {
printf("%c",s[k][j]);
if(num[k+1][j]==-1 || k+1==r) {
printf("\n");
break;
}
}
}
}
}
}
return 0;
}
习题3-7 DNA序列(DNA Consensus String, ACM/ICPC Seoul 2006, UVa1368)
输入m个长度均为n的DNA序列,求一个DNA序列,到所有序列的总Hamming距离尽量小。两个等长字符串的Hamming距离等于字符不同的位置个数,例如,ACGT和GCGA的Hamming距离为2(左数第1, 4个字符不同)。
输入整数m和n(4≤m≤50, 4≤n≤1000),以及m个长度为n的DNA序列(只包含字母A,C,G,T),输出到m个序列的Hamming距离和最小的DNA序列和对应的距离。
#include<stdio.h>
#include<string.h>
int main() {
int T;
scanf("%d",&T);
while(T--) {
int m,n;
scanf("%d %d",&m,&n);
char s[m][n];
for(int i=0;i<m;i++) {
scanf("%s",s[i]);
}
char ss[n];
memset(ss, 'x', sizeof(ss));
int p=0;
int hamming=0;
for(int j=0;j<n;j++) {
int a=0,c=0,g=0,t=0;
for(int i=0; i<m; i++) {
if(s[i][j]=='A') {
a++;
} else if(s[i][j]=='C') {
c++;
} else if(s[i][j]=='G') {
g++;
} else{
t++;
}
}
int maxNum=a;
char temp='A';
if(c>maxNum) {
maxNum=c;
temp='C';
}
if(g>maxNum) {
maxNum=g;
temp='G';
}
if(t>maxNum) {
maxNum=t;
temp='T';
}
ss[p++]=temp;
if(temp=='A') hamming+=(c+g+t);
if(temp=='C') hamming+=(a+g+t);
if(temp=='G') hamming+=(c+a+t);
if(temp=='T') hamming+=(c+g+a);
}
for(int i=0;i<n;i++){
printf("%c",ss[i]);
}
printf("\n%d\n",hamming);
}
return 0;
}
习题3-8 循环小数(Repeating Decimals, ACM/ICPC World Finals 1990, UVa202)
输入整数a和b(0≤a≤3000,1≤b≤3000),输出a/b的循环小数表示以及循环节长度。例如a=5,b=43,小数表示为0.(116279069767441860465),循环节长度为21。
不会,网上查的也看不懂。高精度,数论,抽屉。
习题3-9 子序列(All in All, UVa 10340)
输入两个字符串s和t,判断是否可以从t中删除0个或多个字符(其他字符顺序不变),得到字符串s。例如,abcde可以得到bce,但无法得到dc。
#include<stdio.h>
#include<string.h>
#define maxn 100000 + 10
char s[maxn],t[maxn];
int main() {
while(scanf("%s%s",s,t)!=EOF){
int i=0,j=0,m=strlen(s),n=strlen(t);
if(m>n){
printf("No\n");
continue;
}
for(;i<n;i++){
if(t[i]==s[j]) {
j++;
}
if(j==m){
break;
}
}
printf("%s\n",j==m?"Yes":"No");
}
return 0;
}