题意:把一个括号表达式 S,从 P 编码转换为 W 编码。
P 编码:P = P1,P2, ... , Pn; 其中 Pi 是 S 的第 i 个右括号前面的左括号个数。
W 编码:W = W1, W2, ... , Wn; 对于 S 中的某一个右括号 a,Wi 是从与 a 匹配的左括号开始数起,数到 a 为止的右括号个数。
例如:S (((()()())))
P-编码: 4 5 6 6 6 6;
W-编码: 1 1 1 4 5 6;
分析:编码的元素个数就是右括号个数,也就是字符串S的一半长度。根据 P 编码建立起 S 字符串,然后根据 W 编码的定义进行转换。这里转换成 W 编码时,虽然是线性扫描,但是存在重复扫描的现象,但并没有利用已经扫描过的信息。潜意识里感觉有可能有优化的余地。不过这个题目规模很小,所以意义不大。
#include <stdio.h> #include <string.h> char str[256]; /* 根据 P 编码建立括号表达式 */ void init_from_p(int* p, int length) { int i, j; int index = 0; int left_count = 0; for(i = 0; i < length; i++) { for(j = 0; j < (p[i] - left_count); j++) { str[index] = '('; ++index; } left_count = p[i]; str[index] = ')'; ++index; } str[index] = '\0'; } /* 根据建立的括号表达式转换到 W 编码 */ void convert_to_w(int* w) { int index = 0, depth = 0; int right_count = 0; char *p = str, *p2; while(*p) { if(*p == ')') { right_count = 1; depth = 1; p2 = p - 1; while(depth > 0) { if(*p2 == ')') { right_count++; depth++; } else if(*p2 == '(') depth--; --p2; } w[index] = right_count; ++index; } ++p; } } int main(int argc, char* argv[]) { int t, n, i, j; int p[24], w[24]; scanf("%ld", &t); for(i = 0; i < t; i++) { scanf("%ld", &n); for(j = 0; j < n; j++) scanf("%ld", p + j); memset(str, 0, sizeof(str)); init_from_p(p, n); convert_to_w(w); for(j = 0; j < (n - 1); j++) printf("%ld ", w[j]); printf("%ld\n", w[j]); } return 0; }
(2)ZOJ 1434. Frogger's For Dinner
题意:一个比较有趣的模拟题目。一只青蛙要横穿过马路,马路中间有双向的 8 车道,上面分布着汽车,用 10 X 10 的网格表示。青蛙可以从选择任何一列向下穿越(之后青蛙只能位于这一列),汽车是“折回式”循环行驶的,即每一行(车道)上的汽车的速度相同(以免发生车祸)。青蛙前进的规则是,青蛙先前进一行,然后所有汽车沿着自己的方向前进 N 格,如果青蛙撞到汽车(重叠),或者汽车走过的轨迹覆盖到了青蛙的位置,则青蛙被杀死。题目要求的本质是判断青蛙是否能够成功穿过马路。
分析:如果有汽车的速度是 9,显然该汽车一次移动轨迹可以完整覆盖所有列,则青蛙必然无法穿过。如果一行上摆满 10 辆汽车,显然青蛙也无法穿过。这可以使我们提前确定答案。注意,事后发现下面的代码中直接对 map 做了实时改动,但是也 AC 了,深层原因我没有去想。实际上每尝试一列,都应该使用 map 的全新副本进行模拟,map 本身不要进行任何变动。
#include <stdio.h> #include <string.h> char map[10][10]; /* run one row down. return 1 if the frog is alive */ /* row, col: the position of the frog */ int run(int frogRow, int frogCol) { char tmpRow[12]; int row, col; int x2; int bAliving = 1; for(col = 0; col < 10; col++) { if(map[frogRow + 1][col] != 0) { if(frogRow + 1 <= 4) x2 = col - map[frogRow + 1][col]; else x2 = col + map[frogRow + 1][col]; /* the frog is killed ? */ if(x2 < 0) { if(frogCol <= col || frogCol >= (x2 + 10)) return 0; } else if(x2 > 9) { if(frogCol >= col || frogCol <= (x2 - 10)) return 0; } else { if((frogCol - col) * (frogCol - x2) <= 0) return 0; } } } /* make the vehicles run */ for(row = 1; row <= 8; row++) { memset(tmpRow, 0, sizeof(tmpRow)); for(col = 0; col < 10; col++) { if(map[row][col] != 0) { if(row <= 4) x2 = col - map[row][col]; else x2 = col + map[row][col]; if(x2 < 0) x2 += 10; else if(x2 > 9) x2 -= 10; tmpRow[x2] = map[row][col]; } } memcpy(map[row], tmpRow, 10); } return 1; } int can_across() { int frogRow, frogCol; for(frogCol = 0; frogCol < 10; frogCol++) { frogRow = 0; while(1) { if(run(frogRow, frogCol)) frogRow++; else break; if(frogRow == 9) return 1; } } return 0; } int main(int argc, char* argv[]) { char line[48]; int i, j, car_count; int bCanAcross; while(gets(line) != NULL) /* START */ { bCanAcross = 1; /* resume the frog can across.*/ memset(map, 0, sizeof(map)); for(i = 1; i <= 8; i++) { gets(line); car_count = 0; for(j = 0; j < 10; j++) { map[i][j] = line[j * 2] - '0'; if(map[i][j] == 9) bCanAcross = 0; /* the car's speed is too fast! */ if(map[i][j] != 0) car_count++; } if(car_count == 10) /* there are too many cars */ bCanAcross = 0; } gets(line); /* END */ if(bCanAcross) bCanAcross = can_across(); /* test which col can across */ if(bCanAcross) printf("LEFTOVER POSSUM\n"); else printf("FROGGER\n"); } return 0; }
(3)ZOJ 1707. Automatic Editing
题意:给出一个字符串,和 n 条查找替换规则。依次应用这些规则,然后输出处理后的结果。注意,1)每次都从头开始搜索要替换的字符串;2)一旦使用完了某条规则(即要查找的单词不再出现),之后就不能再次使用这条规则。3)大小写重要。
#include <stdio.h> #include <string.h> char find[10][96]; char replace[10][96]; void edit(int n, char* text, char* result) { int i, len; char tmp[256]; char* p = NULL; strcpy(tmp, text); for(i = 0; i < n; i++) { len = strlen(find[i]); while((p = strstr(tmp, find[i])) != NULL) { *p = 0; sprintf(result, "%s%s%s", tmp, replace[i], p + len); strcpy(tmp, result); } } } int main(int argc, char* argv[]) { char line[256], result[256]; int n, i; while(1) { scanf("%ld", &n); if(n == 0) break; getchar(); /* discard the newline char */ for(i = 0; i < n; i++) { gets(find[i]); gets(replace[i]); } gets(line); edit(n, line, result); printf("%s\n", result); } return 0; }
题意:一个由 n 个整数组成的数列,如果相邻元素的差值的绝对值恰好为 1 到 n-1,则该数列被称称为 jolly jumper。
例如:[ 1 4 2 3 ] 是一个 jolly jumper,因为其相邻元素的差值的绝对值分别是 3,2,1。只包含一个元素的数列也定义为 jolly jumper。判断一个给定的数列是否是 jolly jumper。(n < 3000 )
#include <stdio.h> #include <string.h> #include <math.h> char flags[3000]; int main(int argc, char* argv[]) { int n, i, lastNum, curNum, tmp; int bFindResult, bIsJolly; while(scanf("%ld", &n) != EOF) { memset(flags, 0, sizeof(flags)); bFindResult = 0; bIsJolly = 1; scanf("%ld", &lastNum); for(i = 1; i < n; i++) { scanf("%ld", &curNum); if(bIsJolly) { tmp = (int)fabs(curNum - lastNum); lastNum = curNum; if(tmp == 0 || tmp > n-1 || flags[tmp]) bIsJolly = 0; else flags[tmp] = 1; } } if(bIsJolly) printf("Jolly\n"); else printf("Not jolly\n"); } return 0; }
(5)ZOJ 1889. Ones (本文重点)
题意:给出一个不能被 2 和 5 整除的整数 n (0 <= n <= 10000),它的某个倍数用 10 进制表示全部是 1 组成。问符合这个条件的倍数数字由多少个 1 组成。
例如:n = 7 时, 7 * 15873 = 111111;包含 6 个 1,因此结果为 6。
分析:可以很明显的看到,该体产生的数字已经远超过整型类型表达的范围。因此不能试图用整型去寻找该倍数。根据题目,我们只需要确定存在这样一个数字,由 m 个 1 组成,它能被 n 整除。因此只需要试验 m 的值,从 1 开始逐渐增加,直到找到第一个可以被 n 整除的数字为止。我们只需要考虑该数字除以 n 的余数即可(即 mod n)。假设 m 时余数为 remain,容易得出 m = m + 1 时,余数为 (remain * 10 + 1) mod n;
#include <stdio.h> int main(int argc, char* argv[]) { int n, result, remain; while(scanf("%ld", &n) != EOF) { remain = 1 - 1 / n * n; result = 1; while(remain != 0) { result++; remain = remain * 10 + 1; remain = remain - remain / n * n; } printf("%ld\n", result); } return 0; }
题意:要求打印出一个分形图。分形图用X字符表示,定义如下表。要求绘制出度数为 n (n <= 7) 的分形图。
degree | box fractal |
1 | X |
2 |
X X X X X |
用 B(n-1) 表示 n-1 度 |
B(n-1) B(n-1) B(n-1) B(n-1) B(n-1) |
注意:任何一行结尾不可以有多余的空格,否则将会导致“Presentation Error”。
分析:采用根据其递归定义,用递归函数初始化出一个模板,然后滤除每一行结尾的空格后输出即可。代码中使用了 5 个对 B(n-1)的调用,这里实际上 5 个子矩阵的内容是完全一样的,因此只需要调用一次左上角的 B(n-1),然后把它拷贝到其他四个位置即可,这样可以大大减少递归调用次数,提高代码效率。此题目度数很小,因此可以不做出优化。
#include <stdio.h> #include <string.h> char map[732][732]; void fractal(int n, int row, int col, int length) { if(n == 1) { map[row][col] = 'X'; return; } else { int k = length / 3; fractal(n-1, row, col, k); fractal(n-1, row, col + k * 2, k); fractal(n-1, row + k, col + k, k); fractal(n-1, row + k * 2, col, k); fractal(n-1, row + k * 2, col + k * 2, k); } } void trim(int length) { int row, col; for(row = 0; row < length; ++row) { for(col = length; col >= 0; --col) if(map[row][col] == 'X') break; map[row][col + 1] = '\0'; } } void print_map(int length) { int row; for(row = 0; row < length; ++row) printf("%s\n", map[row]); printf("-\n"); } int main(int argc, char* argv[]) { int length[] = { 0, 1, 3, 9, 27, 81, 243, 729 }; int n; while(scanf("%ld", &n) != EOF) { if(n < 0) break; memset(map, ' ', sizeof(map)); fractal(n, 0, 0, length[n]); trim(length[n]); print_map(length[n]); } return 0; }
题意:字符串处理题目。一个字符串 122344111 可以被描述为“1个1,2个2,1个3,2个4,3个1”。因此,其后继是“1122132431”。类似的,1111111111 后继是 101。通常一个字符串的前继不是唯一的,例如 112213243 个 1 组成的数字,后继也是 1122132431。给出一个字符串,输出它的后继。
#include <stdio.h> void look_say(char *text) { char *p = text; int count = 0; while(*p) { count = 1; while(p[1] == p[0]) { ++count; ++p; } printf("%ld%c", count, *p); ++p; } } int main(int argc, char* argv[]) { char line[1024]; int n, i; scanf("%ld\n", &n); for(i = 0; i < n; i++) { gets(line); look_say(line); printf("\n"); } return 0; }
题意:给出方程 ax2+bxy+cy2+dx+ey+f=0 的系数,判断该曲线是圆,椭圆,双曲线还是抛物线。
分析:此题比较坑爹,反复提交失败后,我忍不住搜索了这个题目。原来方程的系数应该定义为浮点数,而不是整数!了解此题后,则此题毫无难度,尽管给了一个比较有趣的平面截双圆锥的图,但纯属科普作用。
#define EQUAL(a, b) (fabs((a)-(b)) < 0.0000001) #include <stdio.h> #include <math.h> int main(int argc, char* argv[]) { int T, i; double a, b, c, d, e, f; scanf("%ld", &T); for(i = 0; i < T; i++) { scanf("%lf %lf %lf %lf %lf %lf", &a, &b, &c, &d, &e, &f); if(EQUAL(a*c, 0)) printf("parabola\n"); else if(a*c > 0) { if(EQUAL(a, c)) printf("circle\n"); else printf("ellipse\n"); } else if(a*c < 0) printf("hyperbola\n"); } return 0; }