1 序
系统的整理下第四章的学习笔记。同上次一样,尽量在不依赖书本的情况下自己先把例题做出来。这次有许多道题代码量都比较大,在例题中我都用纯C语言编写,但由于习题的挑战性和复杂度,我最终还是决定在第五章开始前,就用C++来完成习题。不过所有的代码都是能在C++提交下AC的。
在习题中,我都习惯性的构造一个类来求解问题,从我个人角度讲,这会让我的思路清晰不少,希望自己的一些代码风格不会影响读者对解题思路的理解。
其实在第四章前,我就顾虑着是不是真的打算把题目全做了,这些题目代码量这么大,要耗费很长一段时间。事实证明我多虑了,一分付出一分收获,经过两周的练习,我的编码能力进步很大。以前比赛一直很怕复杂麻烦的模拟题,现在遇到模拟题反而很开心,因为我算法题会的很少啊~~~
我是从前往后花了两周把题目做一遍的,然后又从后往前整理出每道题的题解,再整理出博客。一些东西弄着弄着可能会没耐心了或心烦,忽悠了事,所以写的不好、有错的地方读者尽管指出,我会修改完善。
建议配合紫书——《算法竞赛入门经典(第2版)》阅读本博客,转载请注明出处:
code4101,谢谢。
2 例题
2.1 UVa1339--Ancient Cipher
思路同书本。只要将两个字符串s1、s2的26个字母分别出现的次数统计出来,存在int a[26]与b[26],然后a、b数组按数值从小到大排序后,看a、b是否相等就能知道能否一一映射。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int cmp(const void* a,const void* b) {
return *(int *)a - *(int *)b;
}
int main() {
char s1[110], s2[110];
while (scanf("%s%s", s1, s2) != EOF) {
int a[26] = {}, b[26] = {}, i;
for (i = 0; s1[i]; i++) a[s1[i]-'A']++;
for (i = 0; s2[i]; i++) b[s2[i]-'A']++;
qsort(a, 26, sizeof(int), cmp);
qsort(b, 26, sizeof(int), cmp);
printf("%s\n", memcmp(a, b, sizeof(a))?"NO":"YES");
}
return 0;
}
2.2 UVa489--Hangman Judge
这题小白书做过,我是用标记数组,来判断哪些字母是否有访问过来求解的。
#include <stdio.h>
int main()
{
int t, i, c;
char s[1000];
bool vis[26], has[26];
while (scanf("%d ", &t), t != -1)
{
for (i = 0; i < 26; i++) vis[i] = has[i] = 0;
scanf("%s", s);
for (i = 0; s[i]; i++) has[s[i]-'a'] = 1;
for (i = 0; i < 26; i++) vis[i] = has[i];
scanf("%s", s);
for (c = i = 0; s[i]; i++)
{
if (has[s[i]-'a']) vis[s[i]-'a'] = 0;
else c++;
if (c == 7) break;
}
for (i = 0; i < 26; i++) if (vis[i]) break;
printf("Round %d\n", t);
if (i == 26 && c <= 7) printf("You win.\n");
else if (i < 26 && c < 7) printf("You chickened out.\n");
else printf("You lose.\n");
}
return 0;
}
2.3 UVa133--The Dole Queue
一开始的思路不太好,我想用一个数组,每有1个或2个离队,就把其值对应删掉(用数组平移,n递减来实现),导致条件分支太多。
其实这题数据非常小,离队的用0值来标记,就不会变的这么麻烦了。
代码是模仿书上的。
其实这题数据非常小,离队的用0值来标记,就不会变的这么麻烦了。
代码是模仿书上的。
#include <stdio.h>
int n, k, m, a[25];
int go(int p, int d, int t) {
while (t--) {
do {
p = (p+d+n-1) % n + 1;
} while (a[p] == 0);
}
return p;
}
int main() {
while (scanf("%d%d%d", &n, &k, &m), n&&k&&m) {
for (int i = 1; i <= n; i++) a[i] = i;
int left = n;
int p1 = n, p2 =1;
while (left) {
p1 = go(p1, 1, k);
p2 = go(p2, -1, m);
printf("%3d", p1); left--;
if (p2 != p1) { printf("%3d", p2); left--;}
a[p1] = a[p2] = 0;
if (left) printf(",");
}
printf("\n");
}
return 0;
}
2.4 UVa213--Message Decoding
我先从LRJ讲的思路和框架入手,然后在根据自己的习惯优化代码。
题目说了编码头(header)只占一行,所以没必要像LRJ的readcodes那么复杂,我直接用my_gets读入header字符串s,然后用init函数与s来初始化code。
题目说了编码头(header)只占一行,所以没必要像LRJ的readcodes那么复杂,我直接用my_gets读入header字符串s,然后用init函数与s来初始化code。
#include <stdio.h>
#include <string.h>
char code[8][1<<8], s[1<<8];
// 过滤空行,返回字符串长度
int my_gets(char* s) {
int k;
while ((k = getchar()) == '\n'); // 空行重读
if (k == -1) k = 0; else gets(s+1);
s[0] = k; return strlen(s);
}
void init() {
int i, j, k = 0;
for (i = 1; ; i++) {
for (j = 0; j < (1<<i)-1; j++) {
code[i][j] = s[k++];
if (!s[k]) return;
}
}
}
// 读取01字符,返回0、1整型值
int read() {
char ch;
while ((ch = getchar()) == '\n');
return ch - '0';
}
// 读取c个01字符
int readint(int c) {
int v = 0;
while (c--) v = (v<<1)+read();
return v;
}
int main() {
int len, v;
while (my_gets(s)) {
init();
while (len = readint(3))
while ((v = readint(len)) != ((1<<len)-1))
putchar(code[len][v]);
putchar('\n');
}
return 0;
}
2.5 UVa512--Spreadsheet Tracking
定义D[51][51][2],D[i][j][0]代表初始位置在第i行第j的元素当前在的行数,D[i][j][1]代表所在列。
其他操作都好写,就是EX的交换操作挂了。WA 5次全是错在交换这里。其实这题自己想出的解题方法还好,主要不是思路不合适的原因。
其他操作都好写,就是EX的交换操作挂了。WA 5次全是错在交换这里。其实这题自己想出的解题方法还好,主要不是思路不合适的原因。
主要问题就是自己没有处理好查找问题,没想过:可不可能找不到?边查边修改值也是兵家之大忌,迫不得已这样操作时要万万小心。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FOR() for (int i = 1; i <= R; i++) for (int j = 1; j <= C; j++)
int R, C, D[51][51][2];
int cmp(const void* a, const void* b) {return *(int *)a-*(int *)b;}
void init() { FOR() D[i][j][0] = i, D[i][j][1] = j; }
void EX() {
int r1, c1, r2, c2;
scanf("%d%d%d%d", &r1, &c1, &r2, &c2);
FOR() if (D[i][j][0]==r1&&D[i][j][1]==c1) {D[i][j][0] = r2; D[i][j][1] = c2;}
else if (D[i][j][0]==r2&&D[i][j][1]==c2) {D[i][j][0] = r1; D[i][j][1] = c1;}
}
void DR(int r) {
FOR() {
if (D[i][j][0] == r) D[i][j][0] = 0;
else if (D[i][j][0] > r) D[i][j][0]--;
}
}
void DC(int c) {
FOR() {
if (D[i][j][1] == c) D[i][j][0] = 0;
else if (D[i][j][1] > c) D[i][j][1]--;
}
}
void IR(int r) { FOR() if (D[i][j][0] >= r) D[i][j][0]++; }
void IC(int c) { FOR() if (D[i][j][1] >= c) D[i][j][1]++; }
int main() {
int kase, cnt, i, j, r, c, a[15], k;
char cmd[5];
for (kase = 1; scanf("%d%d",&R,&C), R; kase++) {
init(); scanf("%d", &cnt);
for (i = 0; i < cnt; i++) {
scanf("%s", cmd);
if (!strcmp(cmd,"EX")) EX();
else {
scanf("%d", &k);
for (j = 0; j < k; j++) scanf("%d", a+j);
qsort(a, k, sizeof(int), cmp);
int dt = 1;
void (*pf)(int); // 函数指针
if (!strcmp(cmd,"DR")) pf = DR, dt = -1;
else if (!strcmp(cmd,"DC")) pf = DC, dt = -1;
else if (!strcmp(cmd,"IR")) pf = IR;
else pf = IC;
for (j = 0; j < k; j++) pf(a[j]+dt*j);
}
}
scanf("%d", &cnt);
if (ka