4.5 注解与习题

到目前为止,本书所需要介绍的C语言知识已经全部讲完了,本章涉及了指针和递归
头文件,副作用及其他
读者应该已经知道main函数也是一个普通的函数(甚至可以递归调用),其返回值将告知操作系统,在算法竞赛中应当总是等于0,唯一的谜团就是#include<stdio.h>
笔者尝试的代码如下

点击查看代码
#include<stdio.h>
int j = 0;
int main(int i)
{
	if(j == 5)
		return 0;
	main(j++);
	printf("hello world!\n");
	return 0;
}

#include<stdio.h>是头文件,实践者的理解方式就是--不加这一行时会出现社么错误,反过来就说明了这一行的作用,不加这一行的编译警告就是:
warning: incompatible implicit declaration of built-in function 'printf' [enabled by default]
也就是说,printf的隐式定义出了问题,这个头文件和printf有关。还记得第一次介绍math.h是怎么讲的吗?如果要使用数学相关的函数,需要包含这个头文件。换句话说,头文件的作用就是:包含了一些函数,供主程序使用。头文件里面并没有printf的源代码,而只有他的声明,printf属于libc的一部分,有兴趣的读者可以自行查阅相关资料。

printf/scanf及其兄弟 格式化输入输出
fopen,freopen,fclose 文件的打开与关闭
getchar,fgets等 字符/字符串输入输出
以上函数的头文件都是stdio.h
sin/cos/pow等(sqrt) 各种数学函数
以上函数的头文件都是math.h
strlen,strcat 字符串函数
memste,memcpy 内存清0与赋值
以上函数的头文件都是string.h
isalpha,isdigit,toupper等 字符分类与转换
以上函数的头文件都是ctype.h
clock
计时函数
以上函数的头文件都是time.h

在编写实用软件的时候,往往需要编写自己的头文件,但在大部分算法竞赛中,只是编写单个程序文件。在本书中,所有题目都由单个程序文件来求解。
下面是一个比较有意思的问题:
是否可以编写一个函数f(),使得依次执行int a = f()和int b = f()的值不同,使用全局变量,这个问题就不会很困难
程序的实现如下:

点击查看代码
#include<stdio.h>

int g = 0;
int f()
{
	g++;
	return g;
}

int main()
{
	int a = f();
	int b = f();
	printf("%d %d\n", a, b);
	return 0;
}

不难写出一个更有意思的程序,写3个函数f(),g(),h(),使得"int a = (f() + g()) + h()"和"int b = f() + (g() + h())"后,a和b的值不同
加法明明满足结合律,居然有可能"(f()+g())+h()"不等于"f()+(g()+h())",这个例子说明:C语言的函数并不都像数学函数那样规矩。或者说得学术一点:C语言的函数可以有副作用,而不像数学函数那样纯,本书无疑深入介绍函数式编程,但时刻警醒最小化副作用是一个良好的编程习惯,正因为如此,前面多次强调,全局变量要少用,因为有时候就会出现上述的C语言中的函数得副作用

再来看一个小问题;函数可以返回指针吗?

点击查看代码
#include<stdio.h>

int* get_pointer()
{
	int a = 3;
	return &a;
}

int main()
{
	return 0;
}
这个程序可以编译通过,不过有一个警告: warning: function returns address of local variable [enabled by default] 意思是函数返回了一个局部变量的地址。前面说过,局部变量是在栈中,函数执行完毕后,局部变量就失效了。严格的讲,指针里保存的地址仍然存在,但不再属于那个局部变量。这是如果修改那个指针指向的内容,程序有可能会崩溃,也可能悄悄地修改了另外一个变量的值,使得程序输出一个莫名其妙的结果。 推荐的写法:如果只是想得到一个指向内容为3的指针,可以把这个指针作为参数,然后在函数里修改他;如果坚持返回一个新的指针,可以使用malloc函数进行动态内存分配。

关于浮点数误差的程序如下

点击查看代码
#include<stdio.h>

int main()
{
	double f;
	for(f = 2; f > 1; f -= 1e-6);
	printf("%.7f\n", f);
	printf("%.7f\n", f / 4);
	printf("%.lf\n", f / 4);
	return 0;
}

输出结果为:
0.9999990
0.2499998
0.2
换句话说,在不断减1e-6的过程中出现了误差,使得循环终止时f并不等于1,而是比1小一点,同时除以4保留一位小数的时候应该四舍五入为0.3的,但是却是0.2
作为竞赛选手来说,有一种方法可以缓解这种情况:加上一个EPS以后再输出。这里的EPS通常取一个比最低精度还要小几个数量级的小实数,例如,要求保留3位小鼠的时候EPS为1e-6.这只是权宜之计,甚至有可能起到反作用(如正确的答案真的是0.499999),但在实践中很好用(毕竟正确答案是0.499999的情况要比0.5要少很多)
也就是说上述的方法也是提供一个尽量贴尽答案的方法,但是并不是万能的,仍然有时候会有出现缺漏的时候

在C语言中的标准输入函数scanf中不可以通过%d来读入字符,因为程序会认为此时已经到了整数的末尾并且不会再读入而是将字符放入缓存区并且下次赋值的时候也会将字符拿出来,程序如下:

点击查看代码
#include<stdio.h>

int main()
{
	int a1, a2, a3, a4;
//	scanf("%d%d%d%d", &a1, &a2, &a3, &a4);
//	printf("%d %c %d %d\n", a1, a2, a3, a4);
	scanf("%d%c%d%c%d", &a1, &a2, &a3, &a2,&a4);
	printf("%d .%c. %d %d\n", a1, a2, a3, a4);
	return 0;
}

input:1a2a3
output:1 .a. 2 3

象棋:
笔者的AC代码如下:

点击查看代码
#include<stdio.h>
#include<string.h>
#include<ctype.h>

int n, bx, by, red[10][3];
int map[12][12];

int GCR(int x, int y, int c, int r, int dic)
{
	int block = 0;
	for(int i = dic; r ? (x+i > 0 && x+i < 11) : (y+i > 0 && y+i < 10); i += dic)
	{
		if((map[x+i*r][y+i*c] == 'G') || (map[x+i*r][y+i*c] == 'R'))
		{
			if(!block)
				return 1;
			else
				block++;
		}
		else if(map[x+i*r][y+i*c] == 'H')
			block++;
		else if(map[x+i*r][y+i*c] == 'C')
		{
			if(block == 1)
			
				return 1;
			else if(!block)
				block++;	
		}
	}
	return 0;
}

int H(int x, int y)
{
	
	int step[8][2] = {-2, 1, -2, -1, 2, 1, 2, -1, 1, -2, -1, -2, 1, 2, -1, 2};//01 23 45 67
	int hx, hy;
	for(int i = 0; i < 8; i++)
	{	
		hx = x+step[i][0], hy = y+step[i][1];
		if(hx > 0 && map[hx][hy] == 'H')
		{
			if(i < 2)
			{
				if(!map[hx+1][hy])
					return 1; 
			}	
			else if(i < 4)
			{
				if(!map[hx-1][hy])
					return 1;
			}	
			else if(i < 6)
			{
				if(!map[hx][hy+1])
					return 1;
			}	
			else	
			{
				if(!map[hx][hy-1])
					return 1;
			}	
		}	
	}
	return 0;
}

int check(int x, int y)
{
	if(GCR(x, y, 1, 0, 1) || GCR(x, y, 1, 0, -1) || GCR(x, y, 0, 1, 1) || GCR(x, y, 0, 1, -1))
		return 0;
	if(H(x, y))
		return 0;
	return 1;	
}

int bug()
{
	for(int i = bx + 1; i < 11; i++)
	{
		if(map[i][by] && map[i][by] != 'G')
			break;
		else if(map[i][by] == 'G')
		{
			printf("NO\n");
			return 1;	
		}		
	}
	return 0;
}

void paint()
{
	for(int i = 1; i < 11; i++)
	{
		for(int j = 1; j < 10; j++)
			printf("%d ", map[i][j]);
		printf("\n");
	}
}
int main()
{
//	freopen("data.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	while(scanf("%d%d%d", &n, &bx, &by) == 3 && n && bx && by)
	{
//		char s[3];
		char ch;
		memset(map, 0, sizeof(map));
		map[bx][by] = 1;
		for(int i = 0; i < n; i++)
		{
			while((ch = getchar()) && !isalpha(ch))	;
			scanf("%d%d", &red[i][1], &red[i][2]);
//			scanf("%s%d%d", s, &red[i][1], &red[i][2]);
//			red[i][0] = (int)s[0];
			red[i][0] = (int)ch;
			map[red[i][1]][red[i][2]] = red[i][0];
		}	
		if(bug())
			continue;
		if(bx > 1)
			if(check(bx - 1, by))
			{
				printf("NO\n");
				continue;
			}
		if(bx < 3)
			if(check(bx + 1, by))
			{
				printf("NO\n");
				continue;
			}
		if(by > 4)
			if(check(bx, by - 1))
			{
				printf("NO\n");
				continue;
			}
		if(by < 6)
			if(check(bx, by + 1))
			{
				printf("NO\n");
				continue;
			}
		printf("YES\n");
	}
	return 0;
}

当然前面因为忘了考虑炮车炮的阻挡情况,导致WA,可以尝试继续抽象,笔者因为强行通过udebug,因此多了一些突兀的代码,破坏了原先的抽象模型,可以自行理解。

正方形:一道比较简单的simulate题目,甚至可以像笔者一样暴力计算,也是可以AC的,主要是理解模拟的含义,笔者的代码如下

点击查看代码
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#define maxn 10000

int n, m, line[maxn];

int check(int sta, int end, int poi, int r)
{
	for(int i = sta; i < end; i++)
	{
		if(!line[(i*r+poi*(!r))*1000 + (i*(!r) + poi*r)*100 + (i*r+poi*(!r)+r)*10 + (i*(!r)+poi*r+!r)])
		{
//			printf("failed %d\n", (i*r+poi*(!r))*1000 + (i*(!r) + poi*r)*100 + (i*r+poi*(!r)+r)*10 + (i*(!r)+poi*r+!r));
			return 1;
		}
			
	}
	return 0;
}

int deal(int fx, int fy, int bx, int by)
{
	if(check(fx, bx, fy, 1)) return 0;
	if(check(fx, bx, by, 1)) return 0;
	if(check(fy, by, fx, 0)) return 0;
	if(check(fy, by, bx, 0)) return 0;
	return 1;
}

int main()
{
//	freopen("data.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	char ch;
	int x, y, sum, tot, cnt = 0;
	while(scanf("%d %d", &n, &m) == 2)
	{
		if(cnt)
			printf("\n**********************************\n\n");
		printf("Problem #%d\n\n", ++cnt);
		tot = 0;
		memset(line, 0, sizeof(line));
		for(int i = 0; i < m; i++)
		{
			while((ch = getchar()) && !isalpha(ch)) ;
			scanf("%d%d", &x, &y);
			if(ch == 'H')
			{
				line[x*1000 + y*100 + x*10 + (y+1)] = 1;
//				printf("%d %d %d\n", x, y, x*1000 + y*100 + x*10 + (y+1));
			}		
			else
			{
				line[y*1000 + x*100 + (y+1)*10 + x] = 1;
//				printf("%d %d %d\n", x, y, y*1000 + x*100 + (y+1)*10 + x);
			}	
		}
		for(int i = 1; i <= n; i++)
		{
			sum = 0;
			for(int j = 1; j <= n - i + 1; j++)
			{
				for(int k = 1; k <= n - i + 1; k++)
				{
					if(deal(j, k, j+i, k+i))
						sum++;
				}
			}
			tot += sum;
			if(sum)
				printf("%d square (s) of size %d\n", sum, i);
		}
		if(!tot)
			printf("No completed squares can be found.\n");
	}
	return 0;
}

黑白棋:注意对于合法棋子的判断,以及下子后对棋盘影响的模拟,代码如下:

点击查看代码
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define maxn 10

char now, map[maxn][maxn], legalmap[maxn][maxn], cmd[maxn];
int dic[8][2] = {-1, -1, 0, -1, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 1};

void paint()
{	
	for(int i = 1; i < 9; i++)
	{
		for(int j = 1; j < 9; j++)
			if(map[i][j] == '-' && legalmap[i][j])
				printf("%c ", now);
			else if(map[i][j] == 'B' || map[i][j] == 'W')
				printf("%c ", map[i][j]);
			else
				printf("%c ", '0');
		printf("\n");
	}
}

int dcheck(int x, int y, int dx, int dy)
{
	int num = 0;
	for(int i = x+dx, j = y+dy; i>0 && i<9 && j>0 && j<9; i+=dx, j+=dy)
	{
		if(map[i][j] == now)
		{
			if(num)
			{
				legalmap[x][y] = 1;
				return 1;
			}
			else
				return 0;
		}
		else if(map[i][j] == '-')
			return 0;
		else
			num++;
	}
	return 0;
}

int check()
{
	int num = 0;
	memset(legalmap, 0, sizeof(legalmap));
	for(int i = 1; i < 9; i++)
	{
		for(int j = 1; j < 9; j++)
		{
			if(map[i][j] == '-')
			{
				for(int k = 0; k < 8; k++)
				{
					if(dcheck(i, j, dic[k][0], dic[k][1]))
					{
						num++;
						break;
					}
				}				
			}
		}
	}	
	return num;
}

void count()
{
	int w = 0, b = 0;
	for(int i = 1; i < 9; i++)
		for(int j = 1; j < 9; j++)
		{
			if(map[i][j] == 'W')
				w++;
			else if(map[i][j] == 'B')
				b++;	
		}	
	printf("Black -%3d White -%3d\n", b, w);
}


void draw(int x, int y)
{
	map[x][y] = now;
	for(int i = 0; i < 8; i++)
	{
		if(dcheck(x, y, dic[i][0], dic[i][1]))
		{
			for(int k = x+dic[i][0], l = y+dic[i][1]; ; k+=dic[i][0], l+=dic[i][1])
			{
				if(map[k][l] == now)
					break;
				else
					map[k][l] = (now == 'W') ? 'W' : 'B';
			}
		}
	}
}

int main()
{
	//freopen("data.in", "r", stdin);
	//freopen("data.out", "w", stdout);
	int t, num, tot = 0;
	scanf("%d", &t);
	while(t--)
	{
		if(tot++)
			printf("\n");
		memset(map, 0, sizeof(map));
		for(int i = 1; i < 9; i++)
			scanf("%s", &map[i][1]);
		while((now = getchar()) && !isalpha(now));
		while(scanf("%s", cmd) == 1 && cmd[0] != 'Q')
		{	
			num = check();
			if(cmd[0] == 'L')
			{
				if(num)
				{
					for(int i = 1; i < 9; i++)
						for(int j = 1; j < 9; j++)
						{
							if(legalmap[i][j])
							{
								if(num!=1)
								{
									printf("(%d,%d) ", i, j);
									num--;
								}
								else if(num == 1)
									printf("(%d,%d)\n", i, j);
							}	
						}
				}
				else
					printf("No legal move.\n");
			}
			else if(cmd[0] == 'P')
			{
				for(int i = 1; i < 9; i++)
					printf("%s\n", &map[i][1]);
			}
			else
			{
				if(legalmap[cmd[1] - '0'][cmd[2] - '0'])
				{	
					draw(cmd[1] - '0', cmd[2] - '0');
					count();
				}
				else
				{
					now = (now == 'W') ? 'B' : 'W';
					draw(cmd[1] - '0', cmd[2] - '0');
					count();
				}
				now = (now == 'W') ? 'B' : 'W';
			}
		}
		for(int i = 1; i < 9; i++)
			printf("%s\n", &map[i][1]);
	}	
	return 0;
}

骰子涂色:注意骰子的123456的相对位置,以及如何从一定顺序来读取骰子上的数字,注意正面和背面的顺序是相反的,代码如下:

点击查看代码
#include<stdio.h>
#define maxn 15
char s[maxn];

int check()
{
	for(int j = 6; j < 12; j++)
	{
//		printf("%d %d %d %d %d %d %d %d %d %d %d %d\n", 0, j, 3, 6+(j+3)%6, 1, j/3*3 + (j-j/3*3+1)%3, 4, 6+(j/3*3 + (j-j/3*3+1)%3+3)%6, 2, j/3*3 + (j-j/3*3+2)%3, 5, 6+(j/3*3 + (j-j/3*3+2)%3+3)%6);
		if(s[0] == s[j] && s[3] == s[6+(j+3)%6])
		{
			if(j < 9)
			{
				if(s[1] == s[j/3*3 + (j-j/3*3+1)%3] && s[4] == s[6+(j/3*3 + (j-j/3*3+1)%3+3)%6] && s[2] == s[j/3*3 + (j-j/3*3+2)%3] && s[5] == s[6+(j/3*3 + (j-j/3*3+2)%3+3)%6])
					return 1;
				else if(s[5] == s[j/3*3 + (j-j/3*3+1)%3] && s[2] == s[6+(j/3*3 + (j-j/3*3+1)%3+3)%6] && s[1] == s[j/3*3 + (j-j/3*3+2)%3] && s[4] == s[6+(j/3*3 + (j-j/3*3+2)%3+3)%6])
					return 1;
				else if(s[4] == s[j/3*3 + (j-j/3*3+1)%3] && s[1] == s[6+(j/3*3 + (j-j/3*3+1)%3+3)%6] && s[5] == s[j/3*3 + (j-j/3*3+2)%3] && s[2] == s[6+(j/3*3 + (j-j/3*3+2)%3+3)%6])
					return 1;
				else if(s[2] == s[j/3*3 + (j-j/3*3+1)%3] && s[5] == s[6+(j/3*3 + (j-j/3*3+1)%3+3)%6] && s[4] == s[j/3*3 + (j-j/3*3+2)%3] && s[1] == s[6+(j/3*3 + (j-j/3*3+2)%3+3)%6])
					return 1;
			}
			else
			{
				if(s[2] == s[j/3*3 + (j-j/3*3+1)%3] && s[5] == s[6+(j/3*3 + (j-j/3*3+1)%3+3)%6] && s[1] == s[j/3*3 + (j-j/3*3+2)%3] && s[4] == s[6+(j/3*3 + (j-j/3*3+2)%3+3)%6])
					return 1;
				else if(s[1] == s[j/3*3 + (j-j/3*3+1)%3] && s[4] == s[6+(j/3*3 + (j-j/3*3+1)%3+3)%6] && s[5] == s[j/3*3 + (j-j/3*3+2)%3] && s[2] == s[6+(j/3*3 + (j-j/3*3+2)%3+3)%6])
					return 1;
				else if(s[5] == s[j/3*3 + (j-j/3*3+1)%3] && s[2] == s[6+(j/3*3 + (j-j/3*3+1)%3+3)%6] && s[4] == s[j/3*3 + (j-j/3*3+2)%3] && s[1] == s[6+(j/3*3 + (j-j/3*3+2)%3+3)%6])
					return 1;
				else if(s[4] == s[j/3*3 + (j-j/3*3+1)%3] && s[1] == s[6+(j/3*3 + (j-j/3*3+1)%3+3)%6] && s[2] == s[j/3*3 + (j-j/3*3+2)%3] && s[5] == s[6+(j/3*3 + (j-j/3*3+2)%3+3)%6])
					return 1;
			}
		}			
	}
	return 0;
}

void change(int x, int y)
{
	char ch;
	ch = s[x];
	s[x] = s[y];
	s[y] = ch;	
}

int main()
{
//	freopen("data.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	while(scanf("%s", s) == 1)
	{
		change(3, 5);
		change(9, 11);
		if(check())
			printf("TRUE\n");			
		else
			printf("FALSE\n");
	}            
	return 0;
}

IP 网络:注意对于输入数据的处理方式,笔者这里采取了最原始的模拟方法,当然也可以采用异或和与运算来实现,只是中间的思维量可能会更大,代码如下:

点击查看代码
#include<stdio.h>
#include<string.h>
#define maxn 1005
int t, len, net[maxn][40], ans[40];

void dset(int i, int num, int start)
{
	for(int j = start+7; j > start-1; j--)
	{
		net[i][j] = num % 2;
		num /= 2;	
	}	 
}

void set()
{
	int x[4];
	memset(net, 0, sizeof(net));
	for(int i = 0; i < t; i++)
	{
		scanf("%d.%d.%d.%d", &x[0], &x[1], &x[2], &x[3]);
		for(int k = 0; k < 4; k++)
			dset(i, x[k], k*8);
	}
}

void deal()
{
	len = 32;
	memcpy(ans, net[0], sizeof(net[0]));
	for(int i = 1; i < t; i++)
	{
		for(int j = 0; j < len; j++)
		{
			if(net[i][j] != ans[j])
			{
				len = j;
				break;
			}
		}
	}
}

void output()
{
	int out[4];
	memset(out, 0, sizeof(out));
	for(int i = 0; i < 32; i+=8)
		for(int j = 0; j < 8; j++)
			out[i/8] = out[i/8]*2 + ans[i+j];
	printf("%d.%d.%d.%d\n", out[0], out[1], out[2], out[3]);
}

int main()
{
	freopen("data.in", "r", stdin);
	freopen("data.out", "w", stdout);
	while(scanf("%d", &t) == 1)
	{
		set();
		deal();
		for(int i = len; i < 32; i++)
			ans[i] = 0;
		output();
		memset(ans, 0, sizeof(ans));
		for(int i = 0; i < len; i++)
			ans[i] = 1;
		output();
	}
	return 0;
}

莫尔斯电码:本题注意对于数据的判断方式:如果有多组数据是完美配对的,那么输出的数据不是任意的而应该是字典序中最小的,同时如果数据的不匹配,其只有可能是删除或添加,不能存在其他的方式,当然笔者对其他的情况也做了一些判断,就是直接输出?这在udebug中也是同样的输出,当然没有测试过删除后是否能够ac,感兴趣的uu们可以去尝试一下,代码段如下:

点击查看代码
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define maxn 0x7fffffff
char ans[15], cmd[85], code[105][15], word[105][15], woco[105][85];
int cnt = 0, num = 0, len[105], lenw[105];

void deal()
{
	for(int i = 0; i < num; i++)
		for(int j = 0; j < strlen(word[i]); j++)
			for(int k = 0; k < cnt; k++)
				if(code[k][0] == word[i][j])
				{
					for(int l = 1; l < len[k]+1; l++)
						woco[i][lenw[i]++] = code[k][l];
					break;
				}
}

int cmp(int x)
{
	int tnum = 0, xlen = (strlen(cmd) > strlen(woco[x])) ? strlen(woco[x]) : strlen(cmd);
	for(int i = 0; i < xlen; i++)
		if(cmd[i] == woco[x][i])
			tnum++;
		else
			return -1;
	return tnum;
}

int stringcmp(int x)
{
	int tlen = strlen(word[x]) > strlen(ans) ? strlen(ans) : strlen(word[x]);
	for(int i = 0; i < tlen; i++)
		if(word[x][i] < ans[i])
			return 1;
		else if(word[x][i] > ans[i])
			return 0;
}

void check()
{
	int numw = 0;
	for(int i = 0; i < num; i++)
		if(!strcmp(cmd, woco[i]))
		{
			if(numw)
			{
				if(stringcmp(i))
					strcpy(ans, word[i]);
			}
			else
				strcpy(ans, word[i]);
			numw++;
		}
	if(numw == 1)
	{
		printf("%s\n", ans);
		return;
	}
	else if(numw > 1)
	{
		printf("%s!\n", ans);
		return;
	}
	else
	{
		int tcmp, add, cmdlen = strlen(cmd), tans;
		memset(ans, 0, sizeof(ans));
		if(cmp(0) == -1)
			tans = maxn;
		else
		{
			tans = (cmp(0) == cmdlen) ? strlen(woco[0]) - cmdlen : cmdlen - cmp(0);
			strcpy(ans, word[0]);
		}	
		for(int i = 1; i < num; i++)
		{
			tcmp = cmp(i);
			if(tcmp == -1)
				continue;
			add = (tcmp == cmdlen) ? strlen(woco[i]) - cmdlen : cmdlen - tcmp;
			if(tans > add)
			{
				tans = add;
				strcpy(ans, word[i]);
			}
		}		
		printf("%s?\n", ans);
		return;
	}
	return;
}

int main()
{
//	freopen("data.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	char ch;
	memset(code, 0, sizeof(code));
	memset(len, 0, sizeof(len));
	memset(woco, 0, sizeof(woco));
	memset(lenw, 0, sizeof(lenw));
	while((ch = getchar()) != '*')
	{
		if(ch == ' ' || ch == '\n')
			continue;
		code[cnt][0] = ch;
		while((ch = getchar()) != '\n')
		{
			if(ch == ' ')
				continue;
			code[cnt][++len[cnt]] = ch;	
		}		
		cnt++;
	}
	while(scanf("%s", word[num]) == 1)
	{
		if(word[num][0] == '*')
			break;
		num++;
	}
	deal();
	while(scanf("%s", cmd) == 1)
	{
		if(cmd[0] == '*')
			break;
		check();
	}
//	printf("%s %s\n%s %s\n", word[0], woco[0], word[1], woco[1]);
	return 0;
}

RAID技术:本题需要有一些raid技术的相关背景,笔者查阅资料后认为RAID技术,就是将多个磁盘变成一个磁盘的技术,也就是说数据的存储不再局限于一个磁盘的读写,对于RAID0来说,它是由两个磁盘组成的,读写速度是一个磁盘的两倍,很好理解,同一时间两个水管出水和一个水管出水的意思,当然因为他没有odd parity奇校验even parity偶校验,所以一个磁盘损坏的时候,数据就没法再复原了,如此RAID1就出现了冗余序列来提高数据存储安全的算法,本题中的是异或算法,也就是说异或算法加上奇校验或者偶校验可以在一个磁盘数据损坏的情况下完美复原该磁盘的内容,当然两个或两个以上的时候就没有办法了
对于本体来说也就是每一列也就是每个磁盘的同一行下的数据的一起异或的结果要全为1(奇校验)或者0(偶校验)
代码实现如下:

点击查看代码
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define maxn 6500
int d, s, b, len, anslen, ans[maxn*8];
char cmd, data[8][maxn]; 

int check(int row)
{
	int cnt, snc, flag;
	for(int i = 0; i < s; i++)
	{
		flag = 0, cnt = -maxn;
		for(int j = 0; j < d; j++)
		{
			if(data[j][row + i] == 'x')
			{
				if(cnt == -maxn)
					cnt = j;		
				else
					return 1;	
			}	
			else if(!flag)
			{
				snc = data[j][row + i] - '0';
				flag++;
			}
			else
			{
				snc = snc ^ (data[j][row + i] - '0');
			}
		}
		if(cnt != -maxn)
		{
			if(cmd == 'O')
				data[cnt][row + i] = (snc == 0) ? '1' : '0';
			else
				data[cnt][row + i] = (snc == 0) ? '0' : '1';
		}
		else
		{
			if(cmd == 'O')
			{
				if(!snc)
					return 1;
			}	
			else
				if(snc)
					return 1;
		}			
	}
	return 0;
}

void copy(int row)
{
	for(int i = 0; i < d; i++)
	{
		if((row/s) % d == i)
			continue;
		for(int j = 0; j < s; j++)
		{
			ans[anslen++] = data[i][row + j] - '0';	
//			printf("%d %d : %d\n", i, row+j, ans[anslen-1]);
		}
	}
}

int worth(int p)
{
	int w = ans[p];
	for(int i = 1; i < 4; i++)
	{
		w = w*2 + ans[p + i];
	}
	return w;
}

void output()
{
//	printf("\n\n");
//	for(int i = 0; i < anslen; i++)
//	{
//		printf("%d", ans[i]);
//	}
//	printf("\n\n");
	printf("valid, contents are: ");
	for(; anslen % 4; anslen++)
		ans[anslen] = 0;
	for(int i = 0; i < anslen; i += 4)
		printf("%X", worth(i));	
	printf("\n");
}

void paint()
{
	printf("%d %d %d %c\n", d, s, b, cmd);
	for(int i = 0; i < d; i++)
	{
		for(int j = 0; j < len; j++)
			printf("%c", data[i][j]);
		printf("\n");
	}
}

int main()
{
//	freopen("data.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	int cnt = 0, flag;
	while(scanf("%d%d%d", &d, &s, &b) == 3 && d)
	{	
		printf("Disk set %d is ", ++cnt);
		len = s * b;
		flag = 1;
		anslen = 0;
		memset(data, 0, sizeof(data));
		memset(ans, 0, sizeof(ans));
		while((cmd=getchar()) && !isalpha(cmd)) ;
		for(int i = 0; i < d; i++)
			for(int j = 0; j < len; j++)
			{
				while((data[i][j] = getchar()) && (data[i][j] == '\n' || data[i][j] == ' '));	
			}
//		paint();
		for(int i = 0; i < b; i++)
		{
			if(check(i*s))
			{
				printf("invalid.\n");
				flag = 0;
				break;
			}	
			else
			{
				copy(i*s);
			}
		}
		if(flag)
			output();
	}	
	return 0;
}
//odd parity 奇校验 even parity 偶校验 raid 中最有趣的是它能通过异或运算来判断出最后一个缺少的数据,同时通过增加数据的冗余性,保证了程序数据的存储安全,一定意义上 

特别困的学生:本体代码段,笔者因为过于自信,没有经过深思熟虑,直接进行模拟算法,结果由于自身的思维漏洞,使源程序变得过于冗余,只是一个为了得到AC的代码,本身的逻辑并不是非常完善,而是仅仅只是对于不同的情况的疯狂特判,也就是对于原先代码的补充过于多了,使得自身的框架结构非常不清晰,等以后有空了再回来改吧:
代码段如下:

点击查看代码
#include<stdio.h>
#include<string.h>
int stu[15][5];

int check(int n)
{
	int cnt = 0;
	long long mul = 1;
	for(int i = 0; i < n; i++)
		mul *= (stu[i][1] + stu[i][2]);
	for(long long i = 0; i <= mul; i++)
	{
		cnt = 0;
		for(int j = 0; j < n; j++)
		{
			if((stu[j][0] + i - 1) % stu[j][3] < stu[j][1])
			{
				cnt++;
				stu[j][4] = 1;
//				printf("%d %d\n", stu[j][3], (stu[j][0] + i - 1) % stu[j][3]);
//				printf("time %d stu %d wake %d cnt\n", i, j, cnt);
			}	
			else
			{
//				printf("time %d stu %d sleep %d cnt\n", i, j, cnt);				
				stu[j][4] = 0;
			}
			if(cnt >= (n+1)/2)
			{
				for(int k = j + 1; k < n; k++)
				{
					if((stu[k][0] + i - 1) % stu[k][3] < stu[k][1])
					{
						cnt++;
						stu[k][4] = 1;
					}	
					else
					{
						stu[k][4] = 0;
					}
				}
				if(cnt == n)
				{
					return i + 1;
				}
				cnt = 0;
				for(int k = 0; k < n; k++)
					cnt += stu[k][4]; 
				for(int k = 1; k < 6; k++)
				{
					for(int l = 0; l < n; l++)
					{
						if(stu[l][4])
							continue;
						else if(((stu[l][0] + i + k - 1) % stu[l][3]) < stu[l][1])
						{
//							printf("aaaaawake%d cnt%d time %d\n", l, cnt, k);
							cnt++;
							stu[l][4] = 1;
						}
//						else
//							break;
						}
						if(cnt == n)
							return i + k + 1;
				}
			}
		}
	
	}	
	return -1;
}

int main()
{
	freopen("data.in", "r", stdin);
	freopen("data.out", "w", stdout);
	int n, kase = 0;
	while(scanf("%d", &n) == 1 && n)
	{
		printf("Case %d: ", ++kase);
		memset(stu, 0, sizeof(stu));
		for(int i = 0; i < n ; i++)
		{
			scanf("%d%d%d", &stu[i][1], &stu[i][2], &stu[i][0]);
			stu[i][3] = stu[i][1] + stu[i][2];
		}	
		printf("%d\n", check(n));
	}
	return 0;
}

数据挖掘:
本题更偏向于数学题,可以稍微将第四个式子进行转化,就会发现sp*n-1是影响sq倒二个元素位置的因素,还有一个就是ab带来的影响了,那么后面只要ab尽可能地小,注意这边有坑,这边的小必须是让k值更小而不是相等,因此a,b无限大是不存在的,因此就有了以下的代码段:

点击查看代码
#include<stdio.h>
#include<math.h>
#define maxn 10000.0
#define maxm 0x7fffffff

int main()
{
	freopen("data.in", "r", stdin);
	freopen("data.out", "w", stdout);
	int n, sp, sq, a, b, k;
	double pq, ans;
	while(scanf("%d%d%d", &n, &sp, &sq) == 3)
	{
		pq = (double)sq / sp;
		k = maxm;
		ans = maxn;
//		printf("pq%lf\n", pq);
		for(int i = 0; i < 32; i++)
		{
			for(int j = 0; j < 32; j++)
			{
				ans = (pow(2, i) + 1)*pow(2, -j);
				if(ans >= pq && floor(ans*(n-1)*sp+sq) < k)
				{
					k = floor(( n - 1 ) * ans * sp + sq);
//					printf("aaaaaaaaaaaaa%daaaaaaaaaaaaa\n", k);
//					printf("%d %d %lf\n", i, j, ans);
					a = i;
					b = j;
				}
//				printf("i %d j %d : %lf\n", i, j, ((pow(2, i)+1)*pow(2, -j)));
			}
		}
		printf("%d %d %d\n", k, a, b);
	}
	return 0;
} 


洪水:笔者采用的是最简单的模拟方法,也就是不断淹没相对露出水面最低的土地,每次判断是否水量大于总水量,最终通过计算的区间来获得答案,当然稍微看了一下洛谷的题解,发现有大佬用二分来解决这个问题,是笔者这个模拟算法的上位替代,从时间复杂度来说比笔者的更好,代码段如下:

点击查看代码
#include<stdio.h>
#include<string.h>
#define EPS 1e-7
int n, m, w, len, map[35][35], order[1005], torder[1005], area[1005];
double ave;

void copy(int start, int num)
{
	memcpy(order+start+1, order+start, sizeof(int)*(len - start));
	memcpy(area+start+1, area+start, sizeof(int)*(len - start));
	order[start] = num;
	area[start] = 100;
//	for(int i = 0; i < len+1; i++)
//		printf("%d %d\n", order[i], area[i]);
	len++;
}

void sort()
{
	len = 0;
	order[len] = map[0][0];
	area[len++] = 100;
	for(int i = 0; i < n; i++)
		for(int j = 0; j < m; j++)
		{
			if(i == 0 && j == 0)
				continue;
//			for(int k = 0; k < len; k++)
//				printf("%d %d %d %d %d\n", i, j, map[i][j], order[k], area[k]);
			if(map[i][j] > order[len-1])
			{
				copy(len, map[i][j]);
				continue;
			}
			if(map[i][j] < order[0])
			{
				copy(0, map[i][j]);
				continue;
			}
			if(map[i][j] == order[0])
			{
				area[0] += 100;
				continue;
			}
			for(int k = 1; k < len; k++)
			{
				if(map[i][j] == order[k])
				{
					area[k] += 100;
					break;
				}	
				else if(map[i][j] < order[k] && map[i][j] > order[k-1])
				{
					copy(k, map[i][j]);
					break;
				}
			}
		}	
}

void deal()
{
	memcpy(torder, order, sizeof(int)*(len));
//	for(int i = 0; i < len; i++)
//		printf("%d\n", torder[i]);
	for(int i = 1; i < len; i++)
		area[i] += area[i-1];
//	for(int i = 0; i < len; i++)
//		printf("%d\n", area[i]);
	for(int i = len - 1; i > 0; i--)
		order[i] -= order[i-1];
//	for(int i = 0; i < len; i++)
//		printf("%d %d\n", torder[i], order[i]);
	for(int i = 1; i < len; i++)
	{
		if(order[i]*area[i-1] >= w)
		{
//			printf("%d %d %d %d\n", order[i], torder[i], area[i-1], w);
			ave = (double) w / area[i-1] + torder[i-1];
			return;
		}	
		else
		{
			w -= order[i]*area[i-1];
//			printf("%d %d %d %d\n", order[i], torder[i], area[i-1], w);
		}
	}
	ave = (double) w / (m*n*100) + torder[len-1];
	return;
}

void output()
{
	int cnt = 0;
	for(int i = 0; i < n; i++)
		for(int j = 0; j < m; j++)
			if(map[i][j] < ave)
				cnt++;
	printf("Water level is %.2lf meters.\n", ave);
	printf("%.2lf percent of the region is under water.\n\n", (double)cnt/(n*m)*100);
}

int main()
{
//	freopen("data.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	int kase = 0;
	while(scanf("%d%d", &n, &m) == 2 && n)
	{
		printf("Region %d\n", ++kase);
		memset(map, 0, sizeof(map));
		memset(order, 0, sizeof(order));
		memset(area, 0, sizeof(area));
		memset(torder, 0, sizeof(torder));
		for(int i = 0; i < n; i++)
			for(int j = 0; j < m; j++)
			{
				scanf("%d", &map[i][j]);
				area[map[i][j]] += 100;
			}
		scanf("%d", &w);
		sort();	
//		for(int i = 0; i < len; i++)
//			printf("%d %d\n", order[i], area[i]);
		deal();
		output();
	}	
	return 0;
}

小结:
指针本书还有很多相关内容这本书是没有介绍的,注意指针和数组不是一回事
递归需要从概念和语言两个方面理解,从概念上,递归就是自己使用自己,递归调用就是自己调用自己,递归定义就是自己定义自己,这里的使用自己可以是直接的也可以是间接的,那么第四章就到这结束了,我们第五章再见。

posted @   banyanrong  阅读(63)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示