第一部分 语言篇 第三章 数组和字符串

到现在2020-01-20,算法竞赛入门经典就告一段落了,实在是太难了,头秃

Tips
1.转义字符
转义字符可以定义八进制和十六进制

八进制:		\o		\oo		\ooo
十六进制:	\xh		\xhh
o/h代表一个八/十六进制数字

2.EOF(end of file)表示文件的结束,其值为-1

注意事项

1、声明数组
int array[max]; 这里的max必须是常数,不能是变量
2、将数组定义在main函数外部的意义:定义在main函数内部,max不能过大,如若过大,程序就会异常退出,而定义在main函数外部,则不会出现这种情况。
3、数组之间是不可以直接赋值的,但是在string.h中可以使用函数memcpy(b, a, sizeof(int)*k将数组a中k个元素复制到数组b中。复制全部元素则可以直接用memcpy(b, a, sizeof(a);
其中,数组a和b的定义为int a[maxn], int b[maxn], int k
技巧:为了合理控制空格的数量,保证开头和结尾没有空格,可以设置一个标志变量first来表示输出的变量是否为第一个。
4、字符串是借助一个字符数组来存储的,字符串数组是以’\0’结尾的。
string.h中相关的函数
一个变量类型
size_t:无符号整型(unsigned int),是sizeof操作符返回的结果类型,在64位系统中为long unsigned int
一个宏
NULL:空指针常量的值
以str开头的函数
size_t strlen(const char *str)
计算字符串str的长度,直到空结束字符",不包括空结束字符。
char*strcpy(char *dest,const char *src)
把src所指向的字符串复制到 dest。
char*strncpy(char *dest,const char *src,size_t n)
同上,把src所指向的字符串的前n个字符复制到dest。
char*strcat(char *dest,const char*src)
把src所指向的字符串追加到dest所指向的字符串的结尾,实现字符串的连接。
char*strncat(char*dest,const char*src,size_t n)
同上,把src所指字符串的前n个字符添加到dest所指字符串的结尾处,并覆盖dest所指字符串结尾的",实现字符串的连接。
char*strchr(const char*str,int c)
在参数str所指向的字符串中搜索第一次出现字符C(无符号字符)的位置,如果5tr中没有c,则返回NULL。
char*strrchr(const char *str,int c)
在参数str所指向的字符串中搜索最后一次出现字符C(无符号字符)的位置,如果str中没有c,则返回NULL。
int strcmp(const char *str1,const char *str2)
把str1所指向的字符串和str2所指向的字符串进行比较,当str1<str2时,返回一个负数;当str1==str2时,返回零;当str1>str2时,返回一个正数。
int strncmp(const char *str1,const char *str2,size_t n)
同上,把str1所指字符串的前n个字符和str2进行比较。
以mem开头的函数
void*memcpy(void*dest,const void*src,size_t n)
从src复制n个字符到dest。
int memcmp(const void *str1,const void *str2,size_t n)
把str1和str2的前n个字节进行比较,其返回值与strcmp相同。
void*memset(void *str,int c,size_t n)
复制字符c(无符号字符)到参数str所指向的字符串的前n个字符。
void*memchr(const void *str,int c,size_t n)
在参数str所指向的字符串的前n个字节中搜索第一次出现字符C(无符号字符)的位置。
5、字符串的读入
scanf("%s")
以空格符或者换行符从缓冲区中读入字符串
char getchar()
从缓冲区读入一个字符,存放在字符数组中,在字符数组的结尾一定要赋值为'\0',声明该位置为字符串的结尾
gets()
以换行符为字符串结束标志从缓冲区中读取字符串
这个函数在最新的编译器中不可使用,会产生较严重漏洞,对程序有不良影响。

题目

题目1:蛇形填数。
在n×n方阵里填入1,2,…,n×n,要求填成蛇形。例如,n=4时方阵为:
10 11 12 1
9 16 13 2
8 15 14 3
7 6 5 4
此处贴出我失败的代码:(F我U真C难K!!)

#include <stdio.h>
#include <string.h>

void snakelike(int n)
{
	//蛇形填数。在n×n方阵里填入1,2,…,n×n,要求填成蛇形。
	int a[10][10]={0};
	int i, j,start, interval,col,row;
	//scanf("%d", &n);
	for (i = 0; i < (n + 1) / 2; i++)//层数
	{
		start = 4 * i * (n - 1);
		interval = n - i*2 - 1;
		for (row = i,col=n-i-1,j=1; row < n-i; row++,j++)//列
		{
		    a[row][i] = start + 3 * interval - j + 2;
			a[row][col] = start+j;
		}
		for (col = n-i-2, j = 1,row=n-i-1 ; col > i; col--, j++)//行
		{
		    a[i][col] = start + 4 * interval - j + 1;
			a[row][col] = start + interval + j +1;
		}
	}
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < n; j++)
			printf("%-2d ", a[i][j]);
		printf("\n");
	}
}
int main(void)
{
    for(int i=4;i<=8;i++)
	{
	    snakelike(i);
	    printf("\n");
	}
	return 0;
}

不说别的,就说说效果吧,泪奔。
在这里插入图片描述
正确的代码

#include<stdio.h>
#include<string.h>
#define maxn 20
int a[maxn][maxn];
int main()
{
	int n, x, y, tot = 0;
	scanf("%d", &n);
	memset(a, 0, sizeof(a));
	tot = a[x=0][y=n-1] = 1;
	while(tot < n*n)
	{
		while(x+1<n && !a[x+1][y]) a[++x][y] = ++tot;
		while(y-1>=0 && !a[x][y-1]) a[x][--y] = ++tot;
		while(x-1>=0 && !a[x-1][y]) a[--x][y] = ++tot;
		while(y+1<n && !a[x][y+1]) a[x][++y] = ++tot;
	}
	for(x = 0; x < n; x++)
	{
		for(y = 0; y < n; y++) printf("%3d", a[x][y]);
		printf("\n");
	}
	return 0;
}

题目2:竖式问题
找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合。输入数字集合(相邻数字之间没有空格),输出所有竖式。每个竖式前应有编号,之后应有一个空行。最后输出解的总数。具体格式见样例输出(为了便于观察,竖式中的空格改用小数点显示,但所写程序中应该输出空格,而非小数点)。
样例输入:
2357
样例输出:

<1>
..775
X..33
-----
.2325
2325.
-----
25575
The number of solutions = 1
int vertical(void)
{
	int count = 0;
	char s[20], buf[99];
	scanf("%s", s);
	for (int abc = 111; abc <= 999; abc++)
		for (int de = 11; de <= 99; de++)
		{
			int x = abc * (de % 10), y = abc * (de / 10), z = abc * de;
			sprintf(buf, "%d%d%d%d%d", abc, de, x, y, z);
			int ok = 1;
			for (int i = 0; i < strlen(buf); i++)
				if (strchr(s, buf[i]) == NULL) ok = 0;
			if (ok)
			{
				printf("<%d>\n", ++count);
				printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d\n\n", abc, de, x, y, z);
			}
		}
	printf("The number of solutions = %d\n", count);
	return 0;
}

题目3 最小生成元

/*
	如果x加上x的各个数字之和得到y,就说x是y的生成元。给出n(1≤n≤100000),求最小
	生成元。无解输出0。例如,n=216,121,2005时的解分别为198,0,1979。
*/
void digitGenerator(void)
{//1~10000最小生成元
	int T, n;
	memset(ans, 0, sizeof(ans));//初始化Init
	for (int m = 1; m < max_ans; m++)
	{//将1~10000进行枚举,找出其最小生成元放入对应的数组中
		int x = m, y = m;
		while (x > 0) {
			y += x % 10;
			x /= 10;
		}
		if (ans[y] == 0 || m < ans[y])
			ans[y] = m;
	}
	scanf_s("%d", &T);
	while (T--)
	{
		scanf_s("%d", &n);
		printf("%d\n", ans[n]);
	}
}

练习

其中第6、7、11、12题俺不会,可太难了,题目都看不懂。
习题3-1 得分(Score, ACM/ICPC Seoul 2005, UVa1585)
给出一个由O和X组成的串(长度为1~80),统计得分。每个O的得分为目前连续出现的O的个数,X的得分为0。例如,OOXXOXXOOO的得分为1+2+0+0+1+0+0+1+2+3。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void question01(void)
{
	char score[90];
	int i, j, total[90] = { 0 }, sum=0;
	scanf("%s", score);
	for (i = 0; score[i] != '\0'; i++)
	{
		if (score[i] == 'O')
		{
			if (i < 1||score[i-1]==0)
				total[i] = 1;
			else if (score[i - 1] != 0)
				total[i] = total[i - 1] + 1;
		}
		else	total[i] = 0;
		sum += total[i];
	}
	printf("%d\n", sum);
}

习题3-2 分子量(Molar Mass, ACM/ICPC Seoul 2007, UVa1586)
给出一种物质的分子式(不带括号),求分子量。本题中的分子式只包含4种原子,分别为C, H, O, N,原子量分别为12.01, 1.008, 16.00, 14.01(单位:g/mol)。例如,C6H5OH的分子量为94.108g/mol。

void question02(void)
{

	float mass[4] = { 12.01,1.008,16.00,14.01 }, m[20] = { 0 }, mm=0;
	char malor[4] = { 'C','H','O','N' };
	char formula[20];
	int i, j;
	scanf("%s", formula);
	for (i = 0; formula[i] != '\0'; i++)
	{
		for (j = 0; malor[j] != '\0' && (malor[j] != formula[i]); j++);//判断当前字符是否为原子
		if (malor[j] == '\0')//不是原子
			m[i - 1] *= (formula[i] - '0');
		else				//是原子
			m[i] = mass[j];
	}
	for (i = 0; formula[i] != '\0'; i++)
		mm += m[i];
	printf("%.3fg/mol\n", mm);
}

习题3-3 数数字(Digit Counting , ACM/ICPC Danang 2007, UVa1225)
把前n(n≤10000)个整数顺次写在一起:123456789101112…数一数0~9各出现多少次 (输出10个整数,分别是0,1,…,9出现的次数)。

void question03(void)
{

	int n, count[10] = { 0 },i,j;
	scanf("%d", &n);
	for (i = 1; i <= n; i++)
	{
		j = i;
		while (j > 0)
		{
			count[j % 10]++;
			j /= 10;
		}
	}
	for (i = 0; i < 10; i++)
		printf("%d   ", count[i]);
}

习题3-4 周期串(Periodic Strings, UVa455)
如果一个字符串可以由某个长度为k的字符串重复多次得到,则称该串以k为周期。例如,abcabcabcabc以3为周期(注意,它也以6和12为周期)。
输入一个长度不超过80的字符串,输出其最小周期。

void question04(void)
{

	char s[90];
	int i, j, k=0, flag=0,length;
	scanf("%s", s);
	length = strlen(s);
	while(!flag)
	{//从1开始寻找当前字符串的最小周期
		flag = 1;//flag作为标记,表示当前k为当前字符串周期
		k++;
		for (j = 0; j < k&&flag; j++)
		{
			for (i = k+j; i < length&&flag ; i += k)//当前字符串为以j为周期的字符串
				if(s[i] != s[i - k])	flag = 0;
		}
	}
	printf("%d\n", k);
}

习题3-5 谜题(Puzzle, ACM/ICPC World Finals 1993, UVa227)
有一个5*5的网格,其中恰好有一个格子是空的,其他格子各有一个字母。一共有4种指令:A, B, L, R,分别表示把空格上、下、左、右的相邻字母移到空格中。输入初始网格和指令序列(以数字0结束),输出指令执行完毕后的网格。如果有非法指令,应输出“Thispuzzle has no final configuration.”,例如,图3-5中执行ARRBBL0后,效果如图3-6所示。

void question05(void)
{

	char puzzle[5][5] = {{'T','R','G','S','J'},{'X','D','O','K','I'},{'M',' ','V','L','N'},{'W','P','A','B','E'},{'U','Q','H','C','F'}};
	char com[100], order[4] = { 'A','B','L','R' }, temper, illegal[] = "This puzzle has no final configuration.";
	int col = 1, row = 2, j, i, change;
	scanf("%s", com);
	for (i = 0; com[i]!='0'; i++)
	{//执行命令,以0结束
		for (j = 0; j < 4 && com[i] != order[j]; j++);//找到命令对应的操作
		if (j == 4) {
			printf("%s\n", illegal);
			exit(-1);
		}
		change = j % 2 ? 1 : -1;//操作对应的变化,上-1下1左-1右1
		if (j <= 1)
		{//上下移动
			if((row==0&&j==0)||(row==4&&j==1)){
				printf("%s\n", illegal);
				exit(-1);
			}
			puzzle[row][col] = puzzle[row+change][col];//对行进行变化,上-1,下+1
			row = row + change;//最终变化的位置
		}
		else
		{//左右移动
			if ((col == 0 && j == 2) || (col == 4 && j == 3)) {
				printf("%s\n", illegal);
				exit(-1);
			}
			puzzle[row][col] = puzzle[row][col+change];//对列进行变化,左-1,右+1
			col = col + change;//记录最终的位置变化
		}
	}
	puzzle[row][col] = ' ';//最后的位置为空
	for (i = 0; i < 5; i++)
	{
		for (j = 0; j < 5; j++)
			printf("%c ", puzzle[i][j]);
		printf("\n");
	}

}

习题3-8 循环小数(Repeating Decimals, ACM/ICPC World Finals 1990, UVa202)
输入整数a和b(0≤a≤3000,1≤b≤3000),输出a/b的循环小数表示以及循环节长度。例如a=5,b=43,小数表示为0.(116279069767441860465),循环节长度为21。

void question08(void)
{

	int a, b, count = 0, decimal[2][100] = { 0 }, i, init[2] = { 0 }, flag=0;
	//decimal记录余数,init记录初始余数和循环开始位置
	scanf("%d%d", &a, &b);
	printf("%d.", a / b);//integer
	init[0] = a % b;//initial remainder
	while (1)//The flag is typical of the number is not a reapteating decimals
	{
		a %= b;//remainder余数
		decimal[1][count] = a;//记录余数
		a *= 10;
		decimal[0][count] = a / b;//记录除数
		if (count && init[0]== a)    break;//初始余数与当前余数相等,循环结束条件
		for (i = 0; i < count; i++)
			if (decimal[1][i] == decimal[1][count])
			{
			    flag=1;
			    if(i)   init[1] = i;
				else    break;//end of for
			}
		if(flag)	break;//end of while
		count++;
	}
	for (i = 0; i < count; i++)
	{
		if (i == init[1])	printf("(");
		printf("%d", decimal[0][i]);
	}
	printf("),%d\n", count - init[1]);
}

习题3-9 子序列(All in All, UVa 10340)
输入两个字符串s和t,判断是否可以从t中删除0个或多个字符(其他字符顺序不变),得到字符串s。例如,abcde可以得到bce,但无法得到dc。

void question09(void)
{

	char s[100], t[100];
	int i, j, flag, index=0, success=1;
	scanf("%s%s", s, t);
	for (i = 0; s[i]&&success; i++)
	{//从字符串s中依次抽取字符,在t中进行寻找,并记录寻找的位置,作为下一次寻找的起始位置
		flag = 0;
		j = index;//记录开始查找位置
		while (t[j]&&!flag)
		{//利用flag进行判断当前是否在字符串t中找到对应字符
			if (s[i] == t[j])
			{//找到
				flag = 1;
				index = j + 1;
			}
			else	j++;//未找到
		}
		if (!t[j]) {//字符串s中仍有字符未找到
			printf("not.\n");
			success = 0;
		}
	}
	if (!s[i])	printf("Yes.\n");
}

习题3-10 盒子(Box, ACM/ICPC NEERC 2004, UVa1587)
给定6个矩形的长和宽wi 和hi (1≤wi ,hi ≤1000),判断它们能否构成长方体的6个面。

void question10(void)
{
	//读入六组数据,判断是否能找到三对相同的数据,能否将12个数据分成三组相同的数字
	int rect[6][2] = { 0 },i,j,data[12];
	int outflag = 1, inflag;
	for (i = 0; i < 6; i++)//读入数据
		for (j = 0; j < 2; j++)
		{
			scanf("%d", &rect[i][j]);
			data[i * 2 + j] = rect[i][j];
		}
	//能否找到三组相同的数据
	//顺序查找outflag记录是否有相同数据,inflag判断每个数据是否有匹配的
	for (i = 0; i < 6&&outflag; i++)
	{
		inflag = 0;
		for (j = 0; j < 6&&!inflag; j++)
		{
			if (rect[i][0] == rect[j][0] && rect[i][1] == rect[j][1])//存在相同数据
				inflag = 1;
		}
		outflag *= inflag;
	}
	if (outflag)//存在三对相同数据
	{//接着判断是否存在三组相同的数字,每组四个
		//先对data进行排序,判断是否可以分为三组
		int k=0;
		for (i = 0; i < 11 ; i++)
		{
			for (j = 0; j < 11-i ; j++)
			{
				if (data[j] > data[j + 1])
				{
					k = data[j];
					data[j] = data[j + 1];
					data[j + 1] = k;
				}
			}
		}
		outflag = 1;
		for (i = 0; (i < 3)&&outflag; i++)
		{
			inflag = 1;
			for (j = 4 * i; (j < 4 * i + 3)&&inflag; j++)
			{
				if (data[j] != data[j + 1])
					inflag = 0;
			}
			outflag *= inflag;
		}
		if (outflag)
			printf("Right!\n");
		else
			printf("Wrong!\n");
	}
	else
		printf("Wrong!\n");
}

习题3-11 换低挡装置(Kickdown, ACM/ICPC NEERC 2006, UVa1588)
给出两个长度分别为n 1 ,n 2 (n 1 ,n 2 ≤100)且每列高度只为1或2的长条。需要将它们放入一个高度为3的容器(如图3-8所示),问能够容纳它们的最短容器长度。

void questio11(void)
{


}

习题3-12 浮点数(Floating-Point Numbers, UVa11809)
计算机常用阶码-尾数的方法保存浮点数。如图3-9所示,如果阶码有6位,尾数有8位,可以表达的最大浮点数为0.111111111 2 ×2 111111 2 。注意小数点后第一位必须为1,所以一共有9位小数。
图3-9 阶码-尾数保存浮点数
这个数换算成十进制之后就是0.9980468752 63 =9.20535763834529410 18 。你的任务是根据这个最大浮点数,求出阶码的位数E和尾数的位数M。输入格式为AeB,表示最大浮点数为A*10 B 。0<A<10,并且恰好包含15位有效数字。输入结束标志为0e0。对于每组数据,输出M和E。输入保证有唯一解,且0≤M≤9,1≤E≤30。在本题中,M+E+2不必为8的整数倍。

void question12(void)
{
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值