算法竞赛入门经典学习笔记(三)——数组和字符串

  • 数组只有在放在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来判断字符是否为字母,类似的还有isdigitisprint等,在ctype.h中定义。
由于ASCII码表中大写字母、小写字母和数字都是连续的,如果ch是大写字母,则ch-'A'就是
它在字母表中的序号(A的序号是0,B的序号是1,依此类推);类似地,如果ch是数字,
则ch-'0'就是这个数字的数值本身(例如'5'-'0'=5)。

头文件ctype.h中定义的isalphaisdigitisprint等工具可以用来判断字符
的属性,而touppertolower等工具可以用来转换大小写。如果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位就是乘以 2 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;
}

猜你喜欢

转载自blog.csdn.net/u011741311/article/details/80782848