算法竞赛入门经典第四章 【uvaoj在线习题(二)】

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=96


uva401

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=96&page=show_problem&problem=342


这个题得了几次RE,千思万想把char *p都初始为NULL了(感觉没啥影响),然后发现主函数没有return 0; 加了上述效果提交AC了,然后去掉return 0;提交再次RE;

【注意】主函数一定要返回0!

题目不难,首先写好主函数,因为镜像回文一定属于回文,所以先判断是否是回文,不是回文又包括了镜像文和 不是镜像文也不是回文(输出为不是回文)

因此主函数写完,剩下的工作就是写几个谓词函数了

find函数:本来是想直接用strchr()的,没想到这个函数返回的是第一次查到到位置,而且这个位置并不是数组的下标,因此效果和find很不一样

看到strchr()比较适合作为查找一个字符是否存在于指定字符串,返回值不是很有用


有一个小细节感觉比较经常遇到——

while (*r != '\0') {
	if (*l != *r)
		break;
	l--;
	r++;
}

这个为什么不写成*l-- != *r++,因为一旦发现不等,r--和r++还是执行了,如果后面你 用到*r == '\0'来区分是通过break跳出while还是通过(*r == '\0')跳出while就不妙了。

好在,这里我换了一种写法,直接通过return直接跳出,这样是写成*l-- != *r++的;


#include <stdio.h>
#include <string.h>
#define N 300

char *selfmirrorch = "AHIMOTUVWXY18";
char real[N] = "AEHIJLMOSXTUVWYZ12358\0";
char mirr[N] = "A3HILJMO2XTUVWY51SEZ8\0";   
char str[N];

int
find(char *s, char ch)
{
	char *p = s;
	
	for (; *p != '\0'; p++) {
		if (*p == ch)
			return p-s;
	}
	return -1;
}

int
isre_palin(char *s)
{
	char *l = NULL, *r = NULL;
	char *tmp = s + strlen(s)/2;
	
	l = tmp-1;
	r = 0 == strlen(s)%2 ? tmp : tmp+1;
	while (*r != '\0') {
		if (*l-- != *r++)
			return 0;
	}
	return 1;
}

int
isbelong_selfmirrorch(char *s)
{
	char *p = s;
	
	for (; *p != '\0'; p++) {
		if (strchr(selfmirrorch, *p) == NULL)
			return 0;
	}
	return 1;
}

int
ismi(char *s)
{
	char *l = NULL, *r = NULL;
	char *tmp = s + strlen(s)/2;
	
	l = tmp-1;
	r = 0 == strlen(s)%2 ? tmp : tmp+1;
	while (*r != '\0') {
		if (find(real, *l) == -1 || find(mirr, *r) == -1)
			return 0;
		if (find(real, *l--) != find(mirr, *r++))
			return 0;
	}
	return 1;
}

int
main(void)
{
	while (scanf("%s", str) != EOF) {
		if (isre_palin(str)) {
			if (isbelong_selfmirrorch(str)) {
				printf("%s -- is a mirrored palindrome.\n\n", str);
			}
			else {
				printf("%s -- is a regular palindrome.\n\n", str);
			}
		} else if (ismi(str)) {
			printf("%s -- is a mirrored string.\n\n", str);
		} else {
			printf("%s -- is not a palindrome.\n\n", str);
		}
	}
	return 0;
}



uva10010

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=96&page=show_problem&problem=951


水题但是做了WA了几次,发现又是忽略掉了每组测试数据的输出数据之间要空一行,空行后AC。

思路:按题意从左上角到右下角开始遍历二维数组,首字母相同就停下来,对八个方向,逐一地 先检查边界条件,若不越界就赋值字符到tmp并封装为字符串,然后用strcmp()作比较,匹配即上面输出结果。

#include <stdio.h>
#include <string.h>
#define N 100

char map[N][N];
char str[N];
char tmp[N];
int save_i, save_j;

int
pos(int n, int m)
{
	int i, j, k, l, x;
	
	for (i = 0; i != n; i++) {
		for (j = 0; j != m; j++) {
			if (map[i][j] == str[0]) {
				int jleft = j+1-strlen(str);
				int iup = i+1-strlen(str);
				int jright = j+strlen(str);
				int idown = i+strlen(str);
				x = 0;
				if (jleft >= 0) {
					for (l = j; l != j-strlen(str); l--) {
						tmp[x++] = map[i][l];
					}
					tmp[x] = '\0';
					if (strcmp(str, tmp) == 0) {
						save_i = i;
						save_j = j;
						return 1;
					}
				}
				x = 0;
				if (iup >= 0 && jleft >= 0) {
					for (k = i, l = j; k != i-strlen(str); k--, l--) {
						tmp[x++] = map[k][l];
					}
					tmp[x] = '\0';
					if (strcmp(str, tmp) == 0) {
						save_i = i;
						save_j = j;
						return 1;
					}
				}
				x = 0;
				if (iup >= 0) {
					for (k = i; k != i-strlen(str); k--) {
						tmp[x++] = map[k][j];
					}
					tmp[x] = '\0';
					if (strcmp(str, tmp) == 0) {
						save_i = i;
						save_j = j;
						return 1;
					}
				}
				x = 0;
				if (iup >= 0 && jright <= m) {
					for (k = i, l = j; l != j+strlen(str); k--, l++) {
						tmp[x++] = map[k][l];
					}
					tmp[x] = '\0';
					if (strcmp(str, tmp) == 0) {
						save_i = i;
						save_j = j;
						return 1;
					}
				}
				x = 0;
				if (jright <= m) {
					for (l = j; l != j+strlen(str); l++) {
						tmp[x++] = map[i][l];
					}
					tmp[x] = '\0';
					if (strcmp(str, tmp) == 0) {
						save_i = i;
						save_j = j;
						return 1;
					}
				}
				x = 0;
				if (idown <= n && jright <= m) {
					for (k = i, l = j; l != j+strlen(str); k++, l++) {
						tmp[x++] = map[k][l];
					}
					tmp[x] = '\0';
					if (strcmp(str, tmp) == 0) {
						save_i = i;
						save_j = j;
						return 1;
					}
				}
				x = 0;
				if (idown <= n) {
					for (k = i; k != i+strlen(str); k++) {
						tmp[x++] = map[k][j];
					}
					tmp[x] = '\0';
					if (strcmp(str, tmp) == 0) {
						save_i = i;
						save_j = j;
						return 1;
					}
				}
				x = 0;
				if (idown <= n && jleft >= 0) {
					for (k = i, l = j; k != i+strlen(str); k++, l--){
						tmp[x++] = map[k][l];
					}
					tmp[x] = '\0';
					if (strcmp(str, tmp) == 0) {
						save_i = i;
						save_j = j;
						return 1;
					}
				}
			}
		}
	}
}

int
main(void)
{
	int tc;
	int n, m, i, j;
	int num;

	scanf("%d", &tc);
	while (tc--) {
		scanf("%d %d", &n, &m);
		for (i = 0; i != n; i++) {
			scanf("%s", str);
			for (j = 0; j != m; j++)
				map[i][j] = isupper(str[j]) ? tolower(str[j]) : str[j];
		}
		scanf("%d", &num);
		for (j = 0; j != num; j++) {
			scanf("%s", str);
			for (i = 0; i != strlen(str); i++) {
				if (isupper(str[i]))
					str[i] = tolower(str[i]);
			}
			if (1 == pos(n, m))
				printf("%d %d\n", save_i+1, save_j+1);
		}
		if (tc != 0)
			printf("\n");
	}
	return 0;
}


因为代码很长而且还经过了调试,一次性写对比较困难!因此去参看了其他人的代码。
得到一种很好的解法:
1、给字符矩阵留一个空白外围!(全局变量默认初始为0,即'\0'),这样就不用检查边界了!
2、八个方向逐一作检查,根据每个方向数组下标的变化规律(常量表)来简化代码,一代码变短,二代码不易错,可读性强!
因为不作边界检查,效率反而更好了。第一份代码是0.012s,这份是0.008s!

#include <stdio.h>
#include <string.h>
#define N 60
#define T 8

char map[N][N];
char str[N];
int posi, posj;

typedef struct point {
	int x;
	int y;
}point;

point go[] = {
	{-1, -1}, {-1, +0}, {-1, +1},
	{+0, -1},           {+0, +1},
	{+1, -1}, {+1, +0}, {+1, +1}
};

int
pos(int n, int m)
{
	int i, j, k, s;
	
	for (i = 1; i != n+1; i++)
		for (j = 1; j != m+1; j++) {
			if (map[i][j] == str[0]) {
				for (k = 0; k != T; k++) {
					for (s = 0; s != strlen(str); s++) {
						if (str[s] != map[i + s*(go[k].x)][j + s*(go[k].y)])
							break;
					}
					if (s == strlen(str)) {
						posi = i;
						posj = j;
						return 1;
					}
				}
			}
		}
}

int
main(void)
{
	int tc;
	int n, m, i, j;
	int num;

	scanf("%d", &tc);
	while (tc--) {
		scanf("%d %d", &n, &m);
		for (i = 1; i != n+1; i++) {
			scanf("%s", str);
			for (j = 1; j != m+1; j++)
				map[i][j] = tolower(str[j-1]);
		}
		scanf("%d", &num);
		for (i = 0; i != num; i++) {
			scanf("%s", str);
			for (j = 0; j != strlen(str); j++)
					str[j] = tolower(str[j]);
			if (1 == pos(n, m))
				printf("%d %d\n", posi, posj);
		}
		printf("%s", tc != 0 ? "\n" : "");
	}
	return 0;
}

常量表简化代码让我想起了以前杭电的一个水题
hdoj2005 第几天?

#include <stdio.h>

int days[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

int
main(void)
{
	int y, m, d;
	int i, ans;
	
	while (scanf("%d/%d/%d", &y, &m, &d) != EOF) {
		for (ans = d, i = 1; i != m; ans += days[i++])
			;
		printf("%d\n", ( m >= 3 && (0==y%400 || (0==y%4 && y%100!=0)) )? ans+1 : ans);
	}
	return 0;
}


uva10361

WA了两次,原因里题意没理解明白,其实题目说的很清楚,句子的格式就是s1<s2>s3<s4>s5。而我却在考虑空格的问题。。。

最后直接看成上述形式,把s2 s3 s4 s5保存起来就搞定了。。。

#include <stdio.h>
#include <string.h>
#define N 110

char fir[N];
char sec[N];
char one[N], two[N], three[N], four[N];

int
main(void)
{
	int tc;
	char *p, *q;
	int i;

	scanf("%d%*c", &tc);
	while (tc--) {
		fgets(fir, sizeof (fir), stdin);
		fgets(sec, sizeof (sec), stdin);
		memset(one, 0, sizeof (one));
		memset(two, 0, sizeof (two));
		memset(three, 0, sizeof (three));
		memset(four, 0, sizeof (four));		
		for (p = fir; *p != '<'; p++)
			;
		for (i=0, p++; *p != '>'; p++)
			one[i++] = *p;
		one[i]='\0';
		for (i=0, p++; *p != '<'; p++)
			two[i++] = *p;
		two[i]='\0';
		for (i=0, p++; *p != '>'; p++)
			three[i++] = *p;
		three[i]='\0';
		for (i=0, p++; *p != '\n'; p++)
			four[i++] = *p;
		four[i]='\0';
		
		for (p = fir; *p != '\n'; p++) {
			if (*p != '<' && *p != '>')
				printf("%c", *p);
		}
		printf("\n");
		for (q = sec; *q != '.'; q++)
			printf("%c", *q);
		printf("%s%s%s%s\n", three, two, one, four);
	}
	return 0;
}

一般用fgets()可以搞定的,getchar()也可以。下面试试getchar()


经常看到有人用getline(),参考别人的开源代码用getline()写了一份

#include <iostream>
#include <cstdio>
using namespace std;

string s, s1, s2, s3, s4, s5;

int
main()
{
	int tc;

	cin >> tc;
	getchar();
	while (tc--) {
		getline(cin, s1, '<');
		getline(cin, s2, '>');
		getline(cin, s3, '<');
		getline(cin, s4, '>');
		getline(cin, s5, '\n');
		getline(cin, s,  '\n');
		cout << s1 << s2 << s3 << s4 << s5 << endl;
		s.erase(s.end()-3, s.end()); //erase the last three character
		cout << s << s4 << s3 << s2 << s5 << endl;
	}
	return 0;
}


uva537

题目比较水,按给出来的假定,只需找到两个等号的位置,读取信息即可。

但要写得简短,就要用好常量表和函数,比如sscanf()。

#include <stdio.h>
#include <string.h>
#define M 3
#define N 1000

char ch[]   = "PUI";
char pref[] = "mkM";
char unit[] = "WVA";
double rate[M] = {0.001, 1000.00, 1000000.00};
double v[M];
char mark[M], line[N], prefch;

int
main(void)
{
	int tc, k, i, j;
	char *p;

	scanf("%d%*c", &tc);
	for (k = 0; k != tc; k++) {
		memset(mark, 0, sizeof (mark));
		fgets(line, sizeof (line), stdin);		
		for (p = line; *p != '\n'; p++)
			if (*p == '=') {
				for (i = 0; ch[i] != *(p-1); i++)
					;
				mark[i] = 1;
				sscanf(p+1, "%lf%c", &v[i], &prefch);
				for (j = 0; j != M; j++)
					if (pref[j] == prefch)
						v[i] *= rate[j];
			}		
		for (i = 0; mark[i] != 0; i++)
			;
		if (0 == i)
			v[0] = v[1]*v[2]; /* P = U*I */
		else if (1 == i)
			v[1] = v[0]/v[2]; /* U = P/I */
		else
			v[2] = v[0]/v[1]; /* I = P/U */
		printf("Problem #%d\n%c=%.2f%c\n\n", k+1, ch[i], v[i], unit[i]);
	}
	return 0;
}


uva10115

采用一般方法,用三位数组存规则,然后把读入的行封装成字符串,最后进行更新(利用字符串处理函数strstr()和strcpy())

strcpy()有溢出的危险,下面改成strncpy()

#include <stdio.h>
#include <string.h>
#define N 100

char rules[N/10][2][N];
char str[N*3];
char tmp[N*3];

void
update(char *d, char *s, char *r)
{
	char *p;

	while ( (p = strstr(d, s)) != NULL ) {
		strcpy(tmp, p+strlen(s));
		strcpy(p, r);
		strcpy(p+strlen(r), tmp);
	}	
}

int
main(void)
{
	int n, i;

	while (scanf("%d%*c", &n) != EOF && n) {
		for (i = 0; i != n; i++) {
			fgets(rules[i][0], sizeof (rules[i][0]), stdin);
			rules[i][0][strlen(rules[i][0])-1] = '\0';
			fgets(rules[i][1], sizeof (rules[i][1]), stdin);
			rules[i][1][strlen(rules[i][1])-1] = '\0';
		}
		fgets(str, sizeof (str), stdin);
		str[strlen(str)-1] = '\0';
		for (i = 0; i != n; i++)
			update(str, rules[i][0], rules[i][1]);
		printf("%s\n", str);
	}
}




核心代码只有三行:

strcpy(tmp, p+strlen(s));
strcpy(p, r);
strcpy(p+strlen(r), tmp);








【小结】
1、判断是否存在往往是一次遍历,如果在主函数中,往往要用到break;此时需注意自增条件的位置,否则可能导致无法判断出口;
推荐使用一个存在函数,这样可以用return语句,快速明朗!
2、审题方面:一定要先构造好主函数符合题意的输出规范!可以用文件读写的形式来快速测试!
3、程序绝不允许用“笨方法”!(当然过分追求精致不利于实现)
笨方法常常就是很多if else,本来是可以用for循环的却不会用!所以LRJ提出的学习目标包括【学会用常量表简化代码】
比如最简单的例子就是输出年月日求该日是该年的第几天?

4、如果不想用<cstdio>使用scanf()和cin混编,可以在主函数加入std::ios::sync_with_stdio (false);

 处理字符串的函数介绍:

http://zhwen.org/tools/clib/string/bcmp.html


你可能感兴趣的:(算法竞赛入门经典第四章 【uvaoj在线习题(二)】)