第四章 函数和递归(例题篇)--算法竞赛入门经典

第四章 函数和递归(例题篇)

知识点一:计算组合数 P63

编写函数,参数是两个非负整数n和m,返回组合数
在这里插入图片描述
其中m<=n<=25。例如,n=25,m=12时答案为5200300。

#include <stdio.h>
long long factorial(int n)
{
	long long m = 1;
	for (int i = 1;i <= n;i++)
	{
		m *= i;
	}
	return m;
}
long long C(int n, int m)
{
	return factorial(n) / (factorial(m)*factorial(n - m));
}
int main()
{
	printf("%ld\n", C(21, 1));
	return 0;
}

很不幸:n=21,m=1的返回值竟然是-1。(中间结果溢出)
解决方法:约分(n!/m!=(m+1)(m+2)…(n+1)n),利用
在这里插入图片描述
的特点。解决溢出的问题。

#include <stdio.h>
long long factorial(int n)
{
	long long m = 1;
	for (int i = 1;i <= n;i++)
	{
		m *= i;
	}
	return m;
}
long long C(int n, int m)
{
	if (m < n - m) m = n - m;
	long long ans = 1;
	for (int i = m + 1;i <= n;i++)
	{
		ans *= i;
	}
	for (int i = 1;i <= n - m;i++)
	{
		ans /= i;
	}
	return ans;
}
int main()
{
	printf("%ld\n", C(21, 1));
	return 0;
}
知识点二:素数判定 P64

根据定义,只被1和它自身整除的、大于1的整数称为素数。

#include <stdio.h>
//n太大的请勿调用
int is_prime(int n)
{
	if (n <= 1)
		return 0;
	for (int i = 2;i*i <= n;i++)//只判断不超过sqrt(n)的整数i
	{
		if (n%i == 0)
		{
			return 0;
		}
	}
	return 1;
}
int main()
{
	printf("%d\n", is_prime(2147483647));
	return 0;
}

n过大,i*i会溢出。
改进的版本

#include <stdio.h>
#include<math.h>
int is_prime(int n)
{
	if (n <= 1)
		return 0;
	int m = floor(sqrt(n) + 0.5);
	for (int i = 2;i <= m;i++)
	{
		if (n%i == 0)
		{
			return 0;
		}
	}
	return 1;
}
int main()
{
	printf("%d\n", is_prime(2147483647));
	return 0;
}
知识点三:段错误与栈溢出 P77

“段”是指二进制文件内的区域,所有某种特定类型信息被保存在里面。可以用size程序(安装MinGW里面有这个程序)得到可执行文件中各个段的大小。test1.cpp可以编译出test1.exe。

//test1.cpp
#include <stdio.h>
int sum(int *a, int n)//***
{
	int ans = 0;
	for (int i = 0;i < n;i++)
	{
		ans += a[i];//***
	}
	return ans;
}
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sum(a + 1, 3));
	return 0;
}

执行size的结果是:
D:>size test1.exe
text data bss dec hex filename
2756 740 224 3720 e88 test1.exe
在可执行文件中,正文段(Text Segment)用于储存指令,数据段(Data Segment)用于储存已初始化的全局变量,BSS段(BSS Segment)用于储存未赋值的全局变量所需的空间。
调用栈并不存储在可执行文件中,而是在运行时创建。调用栈所在的段称为堆栈段(Stack Segment)。和其他段一样,堆栈段也有自己的大小,不能被越界。这种情况叫做栈溢出(Stack Overflow)。
局部变量也是放在堆栈段的,所有建议“把较大的数组放在main函数外”。

例题4-1:古老的密码(Ancient Cipher,UVa1339) P73

给定两个长度一样且不超过100的字符串,判断是否能把其中一个字符串的各个字母重排,之后对26个字母做一个一一映射,使得两个字符串相同。例如,JWPUDJSTVP重排后可以得到WJDUPSJPVT,之后把每个字母映射到它的前面一个字母,得到VICTORIOUS,输入两个字符串,输出YES或者NO。
思路:
因为字母可以重排,每个字母的位置并不重要,重要的是每个字母出现的次数;
①统计两个字符串每个字母出现的次数,得到两个数组cnt1[26],cnt2[26];
②之后我们排序下,排序之后结果相同,说明我们输入的两个字符串就可以通过重排一一映射变得相同了,所以本道题的核心在于【排序】学习笔记:
关于排序
C语言中stdlib.h中又一个叫qsort的库函数,
qsort的声明void qsort(void *base, size_t num, size_t size, int(*cmp)(const void *
, const void *));
如果排序是整型数组的话,
int cmp(const void *a, const void *b)
{
return *(int *)a - *(int *)b;
}

#include <stdio.h>
#include<string.h>
#include<stdlib.h>
int cmp(const void *a, const void *b)
{
	return *(int *)a - *(int *)b;
}
int main()
{
	char a[101], b[101];
	int len_s1, len_s2;
	while (scanf("%s%s", a, b) != EOF)
	{
		int len_a, len_b;
		len_a = strlen(a);
		len_b = strlen(b);
		if (len_a != len_b)
		{
			printf("NO\n");
			continue;
		}
		int cnt1[26], cnt2[26];
		memset(cnt1, 0, sizeof(cnt1));
		memset(cnt2, 0, sizeof(cnt2));
		for (int i = 0;i < len_a;i++)
		{
			cnt1[a[i] - 'A']++;
			cnt2[b[i] - 'B']++;
		}
		qsort(cnt1, 26, sizeof(cnt1[0]), cmp);
		qsort(cnt2, 26, sizeof(cnt2[0]), cmp);
		bool flag = true;
		for (int i = 0;i < 26;i++)
		{
			if (cnt1[i] != cnt2[i])
			{
				flag = false;
				break;
			}
		}
		if (flag)
		{
			printf("YES\n");
		}
		else
		{
			printf("NO\n");
		}
	}
	return 0;
}
例题4-2:刽子手游戏(Hangman Judge,UVa489) P79

刽子手游戏其实是一款猜单词游戏,游戏规则是这样的:计算机想一个单词让你猜,你每次可以猜一个字母。如果单词里有那个字母,所有该字母会显示出来;如果没有那个字母,则计算机会在一幅“刽子手”画上填一笔。这幅画一共需要7笔就能完成,因此你最多只能错6次。注意,猜一个已经猜过的字母也算错。
在本题中,你的任务是编写一个“裁判”程 序,输入单词和玩家的猜测,判断玩家赢了 (You win.)、输了(You lose.)还是放弃了 (You chickened out.)。每组数据包含3行,第1行是游戏编号(-1为输入结束标记),第2行是计算机想的单词,第3行是玩家的猜测。后两行保证只含小写字母。
样例输入:
1
cheese
chese
2
cheese
abcdefg
3
cheese
abcdefgij
-1
样例输出:
Round 1
You win.
Round 2
You chickened out.
Round 3
You lose.

#include <stdio.h>
#include<string.h>
#define maxn 100
int main()
{
	freopen("input.txt", "rb", stdin);
	int firstLine;
	char word[maxn], geuss[maxn];
	int len_word, len_geuss;
	int Round = 0;
	while (scanf("%d%s%s", &firstLine, word, geuss) == 3 && firstLine != -1)
	{
		len_word = strlen(word);
		len_geuss = strlen(geuss);
		int count = len_word;
		int restCount = 6;
		for (int i = 0;i < len_geuss;i++)
		{
			int old_count = count;
			for (int j = 0;j < len_word;j++)
			{
				if (word[j] == geuss[i])
				{
					word[j] = '\0';
					count--;
				}
			}
			if (old_count == count)
			{
				restCount--;
				if (restCount < 0)
				{
					printf("Round %d\nYou lose.\n", ++Round);
					break;
				}
			}
			if (count == 0)
			{
				printf("Round %d\nYou win.\n", ++Round);
				break;
			}
		}
		if (count > 0 && restCount >= 0)
		{
			printf("Round %d\nYou chickened out.\n", ++Round);
		}
	}
	return 0;
}
例题4-3:救济金发放(The Dole Queue,UVa133) P82

n(n<20)个人站成一圈,逆时针编号为1~n。有两个官员,A从1开始逆时针数,B从n开始顺时针数。在每一轮中,官员A数k个就停下来,官员B数m个就停下来(注意有可能两个官员停在同一个人上)。接下来被官员选中的人(1个或者2个)离开队伍。
输入n,k,m输出每轮里被选中的人的编号(如果有两个人,先输出被A选中的)。例如,n=10,k=4,m=3,输出为4 8, 9 5, 3 1, 2 6, 10, 7。注意:输出的每个数应当恰好占3列。

#include <stdio.h>
#define maxn 100
int main()
{
	/*freopen("input.txt", "rb", stdin);*/
	int n, k, m;
	if (scanf("%d%d%d", &n, &k, &m) != 3 || n <= 0)
	{
		return 0;
	}
	int a[maxn];
	for (int i = 0;i < n;i++)
	{
		a[i] = i + 1;
	}
	int left = n;
	int p = -1, q = n;
	while (left)
	{
		int pn = k, qn = m;
		while (pn)
		{
			p = (p + 1) % n;
			if (a[p] != 0)
			{
				pn--;
			}
		}
		while (qn)
		{
			q = (q + n - 1) % n;
			if (a[q] != 0)
			{
				qn--;
			}
		}

		printf("%d", a[p]);
		left -= 1;
		if (q != p)
		{
			printf(" %d", a[q]);
			a[p] = 0;
			left -= 1;
		}
		a[p] = a[q] = 0;
		if (left)
		{
			printf(",");
		}
	}
	printf("\n");
	return 0;
}
例题4-4:信息解码(Message Decoding,UVa213) P83

考虑下面的01串序列:
0, 00, 01, 10, 000, 001, 010, 011, 100, 101, 110, 0000, 0001, …, 1101, 1110, 00000, …
首先是长度为1的串,然后是长度为2的串,依此类推。如果看成二进制,相同长度的后 一个串等于前一个串加1。注意上述序列中不存在全为1的串。
你的任务是编写一个解码程序。首先输入一个编码头(例如AB#TANCnrtXc),则上述序列的每个串依次对应编码头的每个字符。例如,0对应A,00对应B,01对应#,…,110对应X,0000对应c。接下来是编码文本(可能由多行组成,你应当把它们拼成一个长长的01 串)。编码文本由多个小节组成,每个小节的前3个数字代表小节中每个编码的长度(用二进制表示,例如010代表长度为2),然后是各个字符的编码,以全1结束(例如,编码长度为2的小节以11结束)。编码文本以编码长度为000的小节结束。
例如,编码头为$#**\,编码文本为0100000101101100011100101000,应这样解码:010(编码长度为2)00(#)00(#)10(*)11(小节结束)011(编码长度为3)000(\)111(小节结束)001(编码长度为1)0($)1(小节结束)000(编码结束)。
输入:
$#**\
0100000101101100011100101000
AB#TANCnrtXc
011110010101111001010100100001011000
输出:
##*\$
XCtA#BBT

#include <stdio.h>
#include <string.h>
int readchar()
{
	for (;;)
	{
		int ch = getchar();
		if (ch != '\n'&&ch != '\r')
		{
			return ch;
		}
	}
}
int readint(int c)
{
	int v = 0;
	while (c--)
	{
		v = v * 2 + readchar() - '0';
	}
	return v;
}
int code[8][1 << 8];
int readcodes()
{
	memset(code, 0, sizeof(code));
	code[1][0] = readchar();
	for (int len = 2;len <= 7;len++)
	{
		for (int i = 0;i < (1 << len) - 1;i++)
		{
			int ch = getchar();
			if (ch == EOF) return 0;
			if (ch == '\n' || ch == '\r')return 1;
			code[len][i] = ch;
		}
	}
	return 1;
}
void printcodes()
{
	for (int len = 1;len <= 7;len++)
	{
		for (int i = 0;i < (1 << len) - 1;i++)
		{
			if (code[len][i] == 0)
				return;
			printf("code[%d][%d]=%c\n", len, i, code[len][i]);
		}
	}
}
int main()
{
	freopen("input.txt", "rb", stdin);
	while (readcodes())
	{
		/*printcodes();*/
		for (;;)
		{
			int len = readint(3);
			if (len == 0)
				break;
			/*printf("len=%d\n", len);*/
			for (;;)
			{
				int v = readint(len);
				/*	printf("v=%d\n", v);*/
				if (v == (1 << len) - 1)
					break;
				putchar(code[len][v]);
			}
		}
		putchar('\n');
	}
	return 0;
}
例题4-5:追踪电子表格中的单元格(Spreadsheet Tracking,UVa512) P85

题目:
https://blog.csdn.net/weixin_43899069/article/details/104370498
输入:
7 9
5
DR 2 1 5
DC 4 3 6 7 9
IC 1 3
IR 2 2 4
EX 1 2 6 5
4
4 8
5 5
7 8
6 5
0 0
输出:
Spreadsheet #1
Cell data in (4,8)moved to (4,6)
Cell data in (5,5)GONE
Cell data in (7,8)GONE
Cell data in (6,5)moved to (1,2)

答案一:

#include <stdio.h>
#include<string.h>
#define maxd 100
#define BIG 10000
int r, c, n, d[maxd][maxd], d2[maxd][maxd], ans[maxd][maxd], cols[maxd];
void copy(char type, int p, int q)
{
	if (type == 'R')
	{
		for (int i = 1;i <= c;i++)
		{
			d[p][i] = d2[q][i];
		}
	}
	else
	{
		for (int i = 1;i < r;i++)
		{
			d[i][p] = d2[i][q];
		}
	}
}
void del(char type)
{
	memcpy(d2, d, sizeof(d));
	int cnt = type == 'R' ? r : c, cnt2 = 0;
	for (int i = 1;i <= cnt;i++)
	{
		if (!cols[i])
		{
			copy(type, ++cnt2, i);
		}
	}
	if (type == 'R')
	{
		r = cnt2;
	}
	else
	{
		c = cnt2;
	}
}
void ins(char type)
{
	memcpy(d2, d, sizeof(d));
	int cnt = type == 'R' ? r : c, cnt2 = 0;
	for (int i = 1;i <= cnt;i++)
	{
		if (cols[i])
		{
			copy(type, ++cnt2, 0);
		}
		copy(type, ++cnt2, i);
	}
	if (type == 'R')
	{
		r = cnt2;
	}
	else
	{
		c = cnt2;
	}
}
int main()
{
	freopen("input.txt", "rb", stdin);
	freopen("output.txt", "wb", stdout);
	int r1, c1, r2, c2, q, kase = 0;
	char cmd[10];
	memset(d, 0, sizeof(d));
	while (scanf("%d%d%d", &r, &c, &n) == 3 && r)
	{
		int r0 = r, c0 = c;
		for (int i = 1;i <= r;i++)
		{
			for (int j = 1;j <= c;j++)
			{
				d[i][j] = i*BIG + j;
			}
		}
		while (n--)
		{
			scanf("%s", cmd);
			if (cmd[0] == 'E')
			{
				scanf("%d%d%d%d", &r1, &c1, &r2, &c2);
				int t = d[r1][c1];d[r1][c1] = d[r2][c2];d[r2][c2] = t;
			}
			else
			{
				int a, x;
				scanf("%d", &a);
				memset(cols, 0, sizeof(cols));
				for (int i = 0;i < a;i++)
				{
					scanf("%d", &x);
					cols[x] = 1;
				}
				if (cmd[0] == 'D')
				{
					del(cmd[1]);
				}
				else
				{
					ins(cmd[1]);
				}
			}
		}
		//后面的代码是查询上面的操作之前表里的数据现在表的那了。
		memset(ans, 0, sizeof(ans));
		for (int i = 1;i <= r;i++)
		{
			for (int j = 1;j <= c;j++)
			{
				ans[d[i][j] / BIG][d[i][j] % BIG] = i*BIG + j;
			}
		}
		if (kase)
		{
			printf("\r\n");
		}
		printf("Spreadsheet #%d\r\n", ++kase);
		scanf("%d", &q);
		while (q--)
		{
			scanf("%d%d", &r1, &c1);
			printf("Cell data in (%d,%d)", r1, c1);
			if (ans[r1][c1] == 0)
			{
				printf("GONE\r\n");
			}
			else
			{
				printf("moved to (%d,%d)\r\n", ans[r1][c1] / BIG, ans[r1][c1] % BIG);
			}
		}
	}
	return 0;
}

答案二:

#include <stdio.h>
#define maxd 10000
struct Command {
	char c[5];
	int r1, c1, r2, c2;
	int a, x[20];
}cmd[maxd];
int r, c, n;
int simulate(int *r0, int *c0)
{
	for (int i = 0;i < n;i++)
	{
		if (cmd[i].c[0] == 'E')
		{
			if (cmd[i].r1 == *r0&&cmd[i].c1 == *c0)
			{
				*r0 = cmd[i].r2;
				*c0 = cmd[i].c2;
			}
			else if (cmd[i].r2 == *r0&&cmd[i].c2 == *c0)
			{
				*r0 = cmd[i].r1;
				*c0 = cmd[i].c1;
			}
		}
		else
		{
			int dr = 0, dc = 0;
			for (int j = 0;j < cmd[i].a;j++)
			{
				int x = cmd[i].x[j];
				if (cmd[i].c[0] == 'I')
				{
					if (cmd[i].c[1] == 'R'&&x <= *r0)
					{
						dr++;
					}
					if (cmd[i].c[1] == 'C'&&x <= *c0)
					{
						dc++;
					}
				}
				else
				{
					if (cmd[i].c[1] == 'R'&&x == *r0)
						return 0;
					if (cmd[i].c[1] == 'C'&&x == *c0)
						return 0;
					if (cmd[i].c[1] == 'R'&&x < *r0)
						dr--;
					if (cmd[i].c[1] == 'C'&&x < *c0)
						dc--;
				}
			}
			*r0 += dr;*c0 += dc;
		}
	}
}
int main()
{
	freopen("input.txt", "rb", stdin);
	freopen("output.txt", "wb", stdout);
	int r0, c0, q, kase = 0;
	while (scanf("%d%d%d", &r, &c, &n) == 3 && r)
	{
		for (int i = 0;i < n;i++)
		{
			scanf("%s", cmd[i].c);
			if (cmd[i].c[0] == 'E')
			{
				scanf("%d%d%d%d", &cmd[i].r1, &cmd[i].c1, &cmd[i].r2, &cmd[i].c2);
			}
			else
			{
				scanf("%d", &cmd[i].a);
				for (int j = 0;j < cmd[i].a;j++)
				{
					scanf("%d", &cmd[i].x[j]);
				}
			}
		}
		if (kase > 0)
			printf("\r\n");
		printf("Spreadsheet #%d\r\n", ++kase);
		scanf("%d", &q);
		while (q--)
		{
			scanf("%d%d", &r0, &c0);
			printf("Cell data in (%d,%d)", r0, c0);
			if (!simulate(&r0, &c0))
				printf("GONE\r\n");
			else
				printf("moved to (%d %d)\r\n", r0, c0);
		}
	}
	return 0;
}
例题4-6:师兄帮帮忙(A Typical Homework,UVa12412) P89

题目:
https://vjudge.net/problem/UVA-12412

#include <stdio.h>
#include<string.h>
#define maxn 1000
#define maxl 100
#define EPS 1e-5
int n = 0;
char sid[maxn][maxl];
int cid[maxn];
char name[maxn][maxl];
int score[maxn][5];
int removed[maxn];

const char *course_name[] = { "Chinese","Mathematics","English","Programming" };
int valid(int k)
{
	for (int i = 0;i < k;i++)
	{
		if (!removed[i])
		{
			if (strcmp(sid[i], sid[k]) == 0)
			{
				return 0;
			}
		}
	}
	return 1;
}
void add()
{
	for (;;)
	{
		printf("Please enter the SID,CID,name and four scores.Enter 0 to finish.\n");
		scanf("%s", sid[n]);
		if (strcmp(sid[n], "0") == 0)
			break;
		scanf("%d%s%d%d%d%d", &cid[n], name[n], &score[n][0], &score[n][1], &score[n][2], &score[n][3]);
		if (valid(n))
		{
			score[n][4] = score[n][0] + score[n][1] + score[n][2] + score[n][3];
			n++;
		}
		else
		{
			printf("Duplicated SID.\n");
		}
	}
}
int rank(int k)
{
	int r = 0;
	for (int i = 0;i < n;i++)
	{
		if (!removed[i] && score[i][4]>score[k][4])
			k++;
	}
	return r + 1;
}
void DQ(int isq)
{
	char s[maxl];
	for (;;)
	{
		printf("Please enter SID or name.Enter 0 to finish.\n");
		scanf("%s", s);
		if (strcmp(s, "0") == 0)break;
		int r = 0;
		for (int i = 0;i < n;i++)
		{
			if (!removed[i])
			{
				if (strcmp(sid[i], s) == 0 || strcmp(name[i], s) == 0)
				{
					if (isq)
					{
						printf("%d %s %d %s %d %d %d %d %d %.2f\n", rank(i), sid[i], cid[i], name[i], score[i][0], score[i][1], score[i][2], score[i][3], score[i][4], score[i][4] / 4.0 + EPS);
					}
					else
					{
						removed[i] = 1;r++;
					}
				}
			}
		}
		if (!isq)
			printf("%d student(s) removed.\n", r);
	}
}
double get_course_stat(int c, int s, int *passed, int *failed)
{
	int tot = 0;
	*passed = *failed = 0;
	for (int i = 0;i < n;i++)
	{
		if (!removed[i] && (c == 0 || cid[i] == c))
		{
			tot += score[i][s];
			if (score[i][s] >= 60)
				(*passed)++;
			else
				(*failed)++;
		}
	}
	return (double)tot / (double)(*passed + *failed);
}
void get_overall_stat(int c, int *cnt)
{
	cnt[0] = cnt[1] = cnt[2] = cnt[3] = cnt[4] = 0;
	for (int i = 0;i < n;i++)
	{
		if (!removed[i] && (c == 0 || cid[i] == c))
		{
			int k = 0;
			for (int j = 0;j < 4;j++)
			{
				if (score[i][j] >= 60)
				{
					k++;
				}
			}
			cnt[k]++;
		}
	}
}
void stat()
{
	int c;
	printf("Please enter class ID,0 for the whole statistics.\n");
	scanf("%d", &c);
	for (int i = 0;i < 4;i++)
	{
		int passed, failed;
		double avg = get_course_stat(c, i, &passed, &failed);
		printf("%s\n", course_name[i]);
		printf("Average Score:%.2f\n", avg + EPS);
		printf("Number of passed students:%d\n", passed);
		printf("Number of failed students:%d\n", failed);
		printf("\n");
	}
	int cnt[5];
	get_overall_stat(c, cnt);
	printf("Overall:\n");
	printf("Number of student who passed all subjects:%d\n", cnt[4]);
	printf("Number of student who passed 3 or more subjects:%d\n", cnt[4] + cnt[3]);
	printf("Number of student who passed 2 or more subjects:%d\n", cnt[4] + cnt[3] + cnt[2]);
	printf("Number of student who passed 1 or more subjects:%d\n", cnt[4] + cnt[3] + cnt[2] + cnt[1]);
	printf("Number of student who failed all subjects:%d\n", cnt[0]);
	printf("\n");
}
int main()
{
	/*freopen("input.txt", "rb", stdin);*/
	for (;;)
	{
		int choice;
		printf("Welcome to Student Performance Management System (SPMS).\n");
		printf("\n");
		printf("1 - Add\n");
		printf("2 - Remove\n");
		printf("3 - Query\n");
		printf("4 - Show ranking\n");
		printf("5 - Show Statistics\n");
		printf("0 - Exit\n");
		printf("\n");

		scanf("%d", &choice);
		if (choice == 0)break;
		if (choice == 1)add();
		if (choice == 2)DQ(0);
		if (choice == 3)DQ(1);
		if (choice == 4)printf("Showing the ranklist hurts students' self-esteem.Don't do that.\n");
		if (choice == 5)stat();
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值