到现在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)
{
}