《算法笔记》学习日记——3.1 简单模拟

3.1 简单模拟

Codeup Contest ID:100000575

问题 A: 剩下的树

题目描述
有一个长度为整数L(1<=L<=10000)的马路,可以想象成数轴上长度为L的一个线段,起点是坐标原点,在每个整数坐标点有一棵树,即在0,1,2,…,L共L+1个位置上有L+1棵树。
现在要移走一些树,移走的树的区间用一对数字表示,如 100 200表示移走从100到200之间(包括端点)所有的树。
可能有M(1<=M<=100)个区间,区间之间可能有重叠。现在要求移走所有区间的树之后剩下的树的个数。
输入
两个整数L(1<=L<=10000)和M(1<=M<=100)。
接下来有M组整数,每组有一对数字。
输出
可能有多组输入数据,对于每组输入数据,输出一个数,表示移走所有区间的树之后剩下的树的个数。
样例输入

4 2
1 2
0 2
11 2
1 5
4 7
0 0

样例输出

2
5

思路
这题比较简单,用数组来模拟一条路,其下标对应端点,下标的值对应是否有树,我这里以0作为有树,以1作为无树,最后遍历整个数组,统计一下有几个0就行了。
代码

#include<stdio.h>
#include<string.h>
int main(){
	int l,m;
	while(scanf("%d%d", &l, &m) != EOF){
		if(l==0&&m==0) break;
		int road[l+1]={0};
		int sum = 0;
		while(m--){
			int a, b;
			scanf("%d%d", &a, &b);
			for(int i=a;i<=b;i++){
				road[i] = 1;
			}
		}
		for(int i=0;i<=l;i++){
			if(road[i]==0) sum++;
		}
		printf("%d\n", sum);
	} 
	return 0;
}

问题 B: A+B

题目描述
给定两个整数A和B,其表示形式是:从个位开始,每三位数用逗号","隔开。
现在请计算A+B的结果,并以正常形式输出。
输入
输入包含多组数据数据,每组数据占一行,由两个整数A和B组成(-109< A,B < 109)。
输出
请计算A+B的结果,并以正常形式输出,每组数据占一行。
样例输入

-234,567,890 123,456,789
1,234 2,345,678

样例输出

-111111101
2346912

思路
很显然这一题是字符串处理的问题,首先要把读入的字符串中的逗号去掉,然后再利用sscanf来快速将字符串转换成整型即可。
代码

#include<stdio.h>
#include<string.h>
int main(){
	char str1[20];
	char str2[20];
	while(scanf("%s%s", str1, str2) != EOF){
		char temp1[20];
		char temp2[20];
		int j, k, a, b;
		j = k = 0;
		//删逗号 
		for(int i=0;i<strlen(str1);i++){
			if(str1[i]!=','){
				temp1[j] = str1[i];
				j++;
			}
		}
		for(int i=0;i<strlen(str2);i++){
			if(str2[i]!=','){
				temp2[k] = str2[i];
				k++;
			}
		}
		//字符串转int
		sscanf(temp1, "%d", &a);
		sscanf(temp2, "%d", &b);
		printf("%d\n", a+b); 
		memset(str1, 0, sizeof(str1)); //每次执行完之后清空所有字符串,准备接收下一次的数据 
		memset(str2, 0, sizeof(str2));
		memset(temp1, 0, sizeof(temp1)); 
		memset(temp2, 0, sizeof(temp2));
	}
	return 0;
}

问题 C: 特殊乘法

题目描述
写个算法,对2个小于1000000000的输入,求结果。特殊乘法举例:123 * 45 = 1 * 4+1* 5+2 * 4+2 * 5 +3 * 4+3 * 5
输入
两个小于1000000000的数。
输出
输入可能有多组数据,对于每一组数据,输出Input中的两个数按照题目要求的方法进行运算后得到的结果。
样例输入

24 65
42 66666
3 67

样例输出

66
180
39

思路
首先,这题还是字符串处理的问题。我们可以把输入的两个数,作为两个字符串存入,然后再拆分字符串中的元素存入int型数组,通过两层for循环就能得到结果了。
代码

#include<stdio.h>
#include<string.h>
int main(){
	char str1[20];
	char str2[20];
	while(scanf("%s%s", str1, str2) != EOF){
		int a[20];
		int b[20];
		for(int i=0;i<strlen(str1);i++){
			a[i] = (int)str1[i]-48;//直接类型转换的话,a[i]存的是str1[i]的ascii码值 
		}
		for(int i=0;i<strlen(str2);i++){
			b[i] = (int)str2[i]-48;
		}
		int sum = 0;
		for(int i=0;i<strlen(str1);i++){
			for(int j=0;j<strlen(str2);j++){
				sum += a[i]*b[j];
			}
		}
		printf("%d\n", sum);
		memset(str1, 0, sizeof(str1));
		memset(str2, 0, sizeof(str2));
		memset(a, 0, sizeof(a));
		memset(b, 0, sizeof(b));
	}
	return 0;
}

问题 D: 比较奇偶数个数

题目描述
第一行输入一个数,为n,第二行输入n个数,这n个数中,如果偶数比奇数多,输出NO,否则输出YES。
输入
输入有多组数据。
每组输入n,然后输入n个整数(1<=n<=1000)。
输出
如果偶数比奇数多,输出NO,否则输出YES。
样例输入

1
67 
7
0 69 24 78 58 62 64

样例输出

YES
NO

思路
这题比较简单,通过while(n–)控制输入的个数,然后设两个记录奇数和偶数个数的变量,最后比较一下哪个多就好了。
代码

#include<stdio.h>
#include<string.h>
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		int odd, even;
		odd = even = 0;
		while(n--){
			int m;
			scanf("%d", &m);
			if(m%2==0) even++;
			else odd++;
		}
		if(even>odd) printf("NO\n");
		else printf("YES\n");
	}
	return 0;
}

问题 E: Shortest Distance (20)

题目描述
The task is really simple: given N exits on a highway which forms a simple cycle, you are supposed to tell the shortest distance between any pair of exits.
输入
Each input file contains one test case. For each case, the first line contains an integer N (in [3, 105]), followed by N integer distances D1 D2 … DN, where Di is the distance between the i-th and the (i+1)-st exits, and DN is between the N-th and the 1st exits. All the numbers in a line are separated by a space. The second line gives a positive integer M (<=104), with M lines follow, each contains a pair of exit numbers, provided that the exits are numbered from 1 to N. It is guaranteed that the total round trip distance is no more than 107.
输出
For each test case, print your results in M lines, each contains the shortest distance between the corresponding given pair of exits.
样例输入

5 1 2 4 14 9
3
1 3
2 5
4 1

样例输出

3
10
7

思路
这道题主要是解决一个环的最短路问题,我认为可以用数组存储路径长,然后先在两个端点间随便走一下,记录此时的路径dis1,然后再取剩下没走过的元素加起来,这就是路径dis2,然后两个变量之间比一下谁比较小就好了。
但是,千万不要在输入端点的循环里面进行距离的计算,那样会造成循环嵌套,是乘法关系,所以时间复杂度最大为N×M=1011,显然超时(见方案1)。
因此,在确定所有端点之间的距离之后,需要把原点1到其他所有端点的距离都算出来(包括顺时针和逆时针),并且分别储存到两个数组中,作为预处理。这样的话,一个while(n–),一个for(i=2;i<=len;i++),一个while(m–),三个循环是加法关系,所以时间复杂度只取最大值,最大就是N的最大值105(见方案2)。
代码

//1:(超时了,时间复杂度是N×M=10^11)
#include<stdio.h>
#include<string.h>
int dis[100001];
int main(){
	int n;
	scanf("%d", &n);
	int i=1;
	while(n--){
		int d;
		scanf("%d", &d);
		dis[i] = d;
		i++;
	}
	int len=i-1;
	int m;
	scanf("%d", &m);
	while(m--){
		int a, b;
		scanf("%d%d", &a, &b);
		int dis1, dis2;
		dis1 = dis2 = 0;
		if(a>b){  //必须保证a是较小者,如果不是,交换 
			int temp = b;
			b = a;
			a = temp;
		}
		for(int i=a;i<b;i++) dis1 += dis[i];
		for(int i=1;i<=a-1;i++) dis2 += dis[i];
		for(int i=b;i<=len;i++) dis2 += dis[i];
		if(dis1>dis2) printf("%d\n", dis2);
		else printf("%d\n", dis1);
	}
	return 0;
}
//2:(时间复杂度是10^5)
#include<stdio.h>
#include<string.h>
int dis[100001];
int clockwise[100001];
int counterclockwise[100001];
int main(){
	int n;
	scanf("%d", &n);
	int i=1;
	while(n--){
		int d;
		scanf("%d", &d);
		dis[i] = d;
		i++;
	}
	int len=i-1;
 //预处理从点1到其他所有点的距离,包括顺时针和逆时针两个方向:
	clockwise[1]=0;
	counterclockwise[len]=dis[len];
	for(int i=2, j=len-1;i<=len, j>=1;i++, j--){
		clockwise[i] = clockwise[i-1] + dis[i-1];
		counterclockwise[j] = counterclockwise[j+1] + dis[j];
	}
	int m;
	scanf("%d", &m);
	while(m--){
		int a, b;
		scanf("%d%d", &a, &b);
		int dis1, dis2;
		dis1 = dis2 = 0;
		if(a>b){  //必须保证a是较小者,如果不是,交换 
			int temp = b;
			b = a;
			a = temp;
		}
		dis1 = clockwise[b] - clockwise[a];
		dis2 = clockwise[a] + counterclockwise[b];
		if(dis1>dis2) printf("%d\n", dis2);
		else printf("%d\n", dis1);
	}
	return 0;
}

问题 F: A+B和C (15)

题目描述
给定区间[-231, 231]内的3个整数A、B和C,请判断A+B是否大于C。
输入
输入第1行给出正整数T(<=10),是测试用例的个数。随后给出T组测试用例,每组占一行,顺序给出A、B和C。整数间以空格分隔。
输出
对每组测试用例,在一行中输出“Case #X: true”如果A+B>C,否则输出“Case #X: false”,其中X是测试用例的编号(从1开始)。
样例输入

4
1 2 3
2 3 4
2147483647 0 2147483646
0 -2147483648 -2147483647

样例输出

Case #1: false
Case #2: true
Case #3: true
Case #4: false

思路
这一题的思路比较清晰,用while(t–)来控制测试用例个数,然后对每组输入的三个数判断并输出即可。但是要注意,区间可以取到231,而int型最大是231-1,所以要用double型或者long long型。
代码

#include<stdio.h>
#include<string.h>
int main(){
	int t;
	while(scanf("%d", &t) != EOF){
		int i=1;
		while(t--){
			double a, b, c;
			scanf("%lf%lf%lf", &a, &b, &c);
			if(a+b>c) printf("Case #%d: true\n", i);
			else printf("Case #%d: false\n", i);
			i++;
		}
	}
	return 0;
}

问题 G: 数字分类 (20)

题目描述
给定一系列正整数,请按要求对数字进行分类,并输出以下5个数字:

  • A1 = 能被5整除的数字中所有偶数的和;
  • A2 = 将被5除后余1的数字按给出顺序进行交错求和,即计算n1-n2+n3-n4…;
  • A3 = 被5除后余2的数字的个数;
  • A4 = 被5除后余3的数字的平均数,精确到小数点后1位;
  • A5 = 被5除后余4的数字中最大数字。

输入
每个输入包含1个测试用例。每个测试用例先给出一个不超过1000的正整数N,随后给出N个不超过1000的待分类的正整数。数字间以空格分隔。
输出
对给定的N个正整数,按题目要求计算A1~A5并在一行中顺序输出。数字间以空格分隔,但行末不得有多余空格。
若其中某一类数字不存在,则在相应位置输出“N”。
样例输入

13 1 2 3 4 5 6 7 8 9 10 20 16 18
8 1 2 4 5 6 7 9 16

样例输出

30 11 2 9.7 9
N 11 2 N 9

思路
这道题主要是将五组数分清楚就好,我第一次做的时候写在一起,愣是写了半天没写出来(捂脸),所以思路是开六个数组,第一组存放输入的原序列,其他五个存放操作数,然后再针对五个数组分别进行对应的操作,可能需要的代码确实长了点,但是好处是思路清晰,不会出错。
代码

#include<stdio.h>
#include<string.h>
int s[1000];
int a1[1000];
int a2[1000];
int a3[1000];
int a4[1000];
int a5[1000];
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		for(int i=0;i<n;i++){
			int temp;
			scanf("%d", &temp);
			s[i] = temp;
		}
		int len1, len2, len3, len4, len5;
		len1 = len2 = len3 = len4 = len5 = 0;
		for(int i=0;i<n;i++){
			if(s[i]%5==0&&s[i]%2==0){
				a1[len1] = s[i];
				len1++;
			}
		}
		for(int i=0;i<n;i++){
			if(s[i]%5==1){
				a2[len2] = s[i];
				len2++;
			}
		}
		for(int i=0;i<n;i++){
			if(s[i]%5==2){
				a3[len3] = s[i];
				len3++;
			}
		}
		for(int i=0;i<n;i++){
			if(s[i]%5==3){
				a4[len4] = s[i];
				len4++;
			}
		}
		for(int i=0;i<n;i++){
			if(s[i]%5==4){
				a5[len5] = s[i];
				len5++;
			}
		}
		if(len1!=0){
			int sum = 0;
			for(int i=0;i<len1;i++) sum += a1[i];
			printf("%d ", sum);
		}
		else printf("N ");
		if(len2!=0){
			int sum = a2[0];
			for(int i=1;i<len2;i++){
				if(i%2==1) sum -= a2[i];
				else sum += a2[i];
			}
			printf("%d ", sum);
		}
		else printf("N ");
		if(len3!=0) printf("%d ", len3);
		else printf("N ");
		if(len4!=0){
			int sum = 0;
			for(int i=0;i<len4;i++) sum += a4[i];
			double av = (double)sum/(double)len4;
			printf("%.1f ", av);
		}
		else printf("N ");
		if(len5!=0){
			int max = 0;
			for(int i=0;i<len5;i++){
				if(a5[i]>max) max = a5[i];
			}
			printf("%d\n", max);
		}
		else printf("N\n");
	}
	return 0;
}

问题 H: 部分A+B (15)

题目描述
正整数A的“DA(为1位整数)部分”定义为由A中所有DA组成的新整数PA。例如:给定A = 3862767,DA = 6,则A的“6部分”PA是66,因为A中有2个6。
现给定A、DA、B、DB,请编写程序计算PA + PB
输入
输入在一行中依次给出A、DA、B、DB,中间以空格分隔,其中0 < A, B < 1010
输出
在一行中输出PA + PB的值。
样例输入

3862767 6 13530293 3
3862767 1 13530293 8

样例输出

399
0

思路
首先,这题是字符串处理问题,把输入的A和B视作字符串,然后遍历寻找DA、DB的值,并赋给空的字符串,然后通过sscanf将新字符串转换为整数再相加即可。这里要注意点的一点是,用scanf读取字符串是会读入空格的,所以在scanf中不要连着写一起。还有,要时刻注意变量的取值范围,1010是大于231-1的,所以不要用int型,并且每次输出结束之后记得清空数组。
代码

#include<stdio.h>
#include<string.h>
int main(){
	char stra[100];
	char strb[100];
	char da,db;
	char strda[100];
	char strdb[100];
	while(scanf("%s %c %s %c", stra, &da, strb, &db) != EOF){
		int len1, len2;
		len1 = len2 = 0;
		for(int i=0;i<strlen(stra);i++){
			if(stra[i]==da){
				strda[len1] = stra[i];
				len1++;
			}
		}
		for(int i=0;i<strlen(strb);i++){
			if(strb[i]==db){
				strdb[len2] = strb[i];
				len2++;
			}
		}
		if(len1==0) strcpy(strda, "0");
		if(len2==0) strcpy(strdb, "0");
		double a, b;
		sscanf(strda, "%lf", &a);
		sscanf(strdb, "%lf", &b);
		printf("%.0f\n", a+b);
		memset(stra, 0, sizeof(stra));
		memset(strb, 0, sizeof(strb));
		memset(strda, 0, sizeof(strda));
		memset(strdb, 0, sizeof(strdb));
	}
	return 0;
}

问题 I: 锤子剪刀布 (20)

题目描述
大家应该都会玩“锤子剪刀布”的游戏:两人同时给出手势,胜负规则如图所示:
在这里插入图片描述
现给出两人的交锋记录,请统计双方的胜、平、负次数,并且给出双方分别出什么手势的胜算最大。
输入
输入第1行给出正整数N(<=105),即双方交锋的次数。随后N行,每行给出一次交锋的信息,即甲、乙双方同时给出的的手势。C代表“锤子”、J代表“剪刀”、B代表“布”,第1个字母代表甲方,第2个代表乙方,中间有1个空格。
输出
输出第1、2行分别给出甲、乙的胜、平、负次数,数字间以1个空格分隔。第3行给出两个字母,分别代表甲、乙获胜次数最多的手势,中间有1个空格。如果解不唯一,则输出按字母序最小的解。
样例输入

10
C J
J B
C B
B B
B C
C C
C B
J B
B C
J J

样例输出

5 3 2
2 3 5
B B

思路
首先,开两个数组记录甲、乙出过的手势,然后还需要两个长度为3的数组分别记录甲、乙获胜最多的手势,我在这里规定下标0对应C,下标1对应J,下标2对应B。
代码

#include<stdio.h>
#include<string.h>
char jia[100000];
char yi[100000];
int jiashoushi[3];
int yishoushi[3];
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		int jiasheng, yisheng, pingju;
		jiasheng = yisheng = pingju = 0;
		int len = 0;
		while(n--){
			char a, b;
			scanf(" %c %c", &a, &b);
			jia[len] = a;
			yi[len] = b;
			len++;
			getchar();//吃掉回车符 
		}
		for(int i=0;i<len;i++){
			if(jia[i]=='C'&&yi[i]=='C'){
				pingju++;
			}
			if(jia[i]=='C'&&yi[i]=='J'){
				jiasheng++;
				jiashoushi[0] += 1;
			}
			if(jia[i]=='C'&&yi[i]=='B'){
				yisheng++;
				yishoushi[2] += 1;
			}
			if(jia[i]=='J'&&yi[i]=='C'){
				yisheng++;
				yishoushi[0] += 1;
			}
			if(jia[i]=='J'&&yi[i]=='J'){
				pingju++;
			}
			if(jia[i]=='J'&&yi[i]=='B'){
				jiasheng++;
				jiashoushi[1] += 1;
			}
			if(jia[i]=='B'&&yi[i]=='C'){
				jiasheng++;
				jiashoushi[2] += 1;
			}
			if(jia[i]=='B'&&yi[i]=='J'){
				yisheng++;
				yishoushi[1] +=1;
			}
			if(jia[i]=='B'&&yi[i]=='B'){
				pingju++;
			}
		}
		int jiamax, jiamax_i, yimax, yimax_i;
		jiamax = jiamax_i = yimax = yimax_i = 0;
		for(int i=0;i<3;i++){
			if(jiashoushi[i]>=jiamax){
				jiamax = jiashoushi[i];
				jiamax_i = i;
			}
		}
		for(int i=0;i<3;i++){
			if(yishoushi[i]>=yimax){
				yimax = yishoushi[i];
				yimax_i = i;
			}
		}
		printf("%d %d %d\n", jiasheng, pingju, yisheng);
		printf("%d %d %d\n", yisheng, pingju, jiasheng);
		if(jiamax==jiashoushi[2]) printf("B ");
		else if(jiamax==jiashoushi[0]) printf("C ");
		else printf("J ");
		if(yimax==yishoushi[2]) printf("B\n");
		else if(yimax==yishoushi[0]) printf("C\n");
		else printf("J\n");
		memset(jia, 0, sizeof(jia));
		memset(yi, 0, sizeof(yi));
		memset(jiashoushi, 0, sizeof(jiashoushi));
		memset(yishoushi, 0, sizeof(yishoushi));
	}
	return 0;
}

小结

本节的题目基本上都没有太大的难度,但是做的时候思路一定要清晰,而且也要细心,比如我最后一题的锤子剪刀布,在写遍历乙手势寻找最大值的时候,因为代码类似,所以直接复制上面遍历甲手势的代码,结果有个地方忘记改了:yimax = jiashoushi[i];,然后就有三个测试点一直过不去(我去PAT和牛客网都交了一遍代码),我也不知道错哪儿了,直到后来看到了这个错误……

另外,在用scanf读字符的时候,一定要在每个%c前面加空格,否则会出错的(比如最后一题的scanf(" %c %c", &a, &b);),而且最后也要加getchar();吃掉回车符。

花时间最久的应该就是PAT甲级的那道Shortest Distance了,其实思路想出来以及代码写出来是很快的,但是时间超限也是很正常的……遇到这种问题的话,应该要注意一下如何消除循环嵌套,一般的方法都是预处理,把需要用到的数据先存在一个数组里,然后再在循环里直接调用,而不是在循环里再循环。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值