程序设计思维与实践 Week2 实验3道

本周实验主要是3道ACM 赛制的模拟题(面向 CSP-T3,赛后补题同等计入平时分),可惜课上一小时五十分钟并未写出,还得多加练习。

A - 化学 (编译器选 GNU G++)

化学很神奇,以下是烷烃基。
在这里插入图片描述
假设如上图,这个烷烃基有6个原子和5个化学键,6个原子分别标号1~6,然后用一对数字 a,b 表示原子a和原子b间有一个化学键。这样通过5行a,b可以描述一个烷烃基

你的任务是甄别烷烃基的类别。

原子没有编号方法,比如
1 2
2 3
3 4
4 5
5 6

1 3
2 3
2 4
4 5
5 6
是同一种,本质上就是一条链,编号其实是没有关系的,可以在纸上画画就懂了.

Input

输入第一行为数据的组数T(1≤T≤200000)。每组数据有5行,每行是两个整数a, b(1≤a,b≤6,a ≤b)

数据保证,输入的烷烃基是以上5种之一

Output

每组数据,输出一行,代表烷烃基的英文名

Sample Input

2
1 2
2 3
3 4
4 5
5 6
1 4
2 3
3 4
4 5
5 6

Sample Output

n-hexane
3-methylpentane

分析

该题主要记录同一个编号出现的次数,即有多少个原子与该编号的原子相连。由此可以区分出三种,其实最终只要区分2-methylpentane与3-methylpentane即可,它们俩是通过出现次数为3的原子的相邻出现次数为1的原子个数为1还是2来区分。

C++

#include<iostream>
using namespace std;

class Pair {
public:
	Pair(int x) :count(x) { next[0] = 0; next[1] = 0; next[2] = 0; next[3] = 0; index = 0; };//构造函数;
	int count;
	int index;//记录next数组中的下标
	int next[4];
};

void output(Pair *sum) {
	int count1 = 0, count0 = 0, target;
	for (int k = 1; k < 7; k++) {
		Pair goal = sum[k];
		if (goal.count == 4) {
			cout << "2,2-dimethylbutane" << endl;
			return;
		}
		else if (goal.count == 3) {
			count0++;
			target = k;
		}
	}
	if (count0 == 0) {
		cout << "n-hexane" << endl;
		return;
	}
	else if (count0 == 1) {
		for (int a = 1; a < 7; a++) {
			for (int c = 0; c < 4; c++) {
				if (sum[a].next[c] == target) {
					if (sum[a].count == 1)
						count1++;
				}
			}
		}
		if (count1 == 2) {
			cout << "2-methylpentane" << endl;
			return;
		}
		else if (count1 == 1) {
			cout << "3-methylpentane" << endl;
			return;
		}
	}
	else if (count0 == 2) {
		cout << "2,3-dimethylbutane" << endl;
		return;
	}
}

int main() {
	int t, x, y;
	cin >> t;
	for (int i = 0; i < t; i++) {
		Pair sum[8] = { Pair(0),Pair(0),Pair(0),Pair(0),Pair(0),Pair(0),Pair(0),Pair(0) };
		for (int j = 0; j < 5; j++) {
			cin >> x >> y;
			sum[x].count++;
			sum[y].count++;
			//之前成了sum[x].next[index++] = y;每经过一层循环下标便加一,致使部分下标的数组为空
			sum[x].next[sum[x].index++] = y;
			sum[y].next[sum[y].index++] = x;
		}
		output(sum);
	}
	return 0;
}

B - 爆零(×)大力出奇迹(√)

程序设计思维作业和实验使用的实时评测系统,具有及时获得成绩排名的特点,那它的功能是怎么实现的呢?
我们千辛万苦怼完了不忍直视的程序并提交以后,评测系统要么返回AC,要么是返回各种其他的错误,不论是怎样的错法,它总会给你记上一笔,表明你曾经在这儿被坑过,而当你历经千辛终将它AC之后,它便会和你算笔总账,表明这题共错误提交了几次。
在岁月的长河中,你通过的题数虽然越来越多,但通过每题时你所共花去的时间(从最开始算起,直至通过题目时的这段时间)都会被记录下来,作为你曾经奋斗的痕迹。特别的,对于你通过的题目,你曾经的关于这题的每次错误提交都会被算上一定的单位时间罚时,这样一来,你在做出的题数上,可能领先别人很多,但是在做出同样题数的人中,你可能会因为罚时过高而处于排名上的劣势。
例如某次考试一共八道题(A,B,C,D,E,F,G,H),每个人做的题都在对应的题号下有个数量标记,负数表示该学生在该题上有过的错误提交次数但到现在还没有AC,正数表示AC所耗的时间,如果正数a跟上了一对括号,里面有个正数b,则表示该学生AC了这道题,耗去了时间a,同时曾经错误提交了b次。例子可见下方的样例输入与输出部分。

Input

输入数据包含多行,第一行是共有的题数n(1≤n≤12)以及单位罚时m(10≤m≤20),之后的每行数据描述一个学生的信息,首先是学生的用户名(不多于10个字符的字串)其次是所有n道题的得分现状,其描述采用问题描述中的数量标记的格式,见上面的表格。

Output

根据这些学生的得分现状,输出一个实时排名。实时排名显然先按AC题数的多少排,多的在前,再按时间分的多少排,少的在前,如果凑巧前两者都相等,则按名字的字典序排,小的在前。每个学生占一行,输出名字(10个字符宽),做出的题数(2个字符宽,右对齐)和时间分(4个字符宽,右对齐)。名字、题数和时间分相互之间有一个空格。数据保证可按要求的输出格式进行输出。

Sample Input

Sample Output

在这里插入图片描述

分析

此题的思路挺简单的,就是一个对象的排序,主要是要注意()的处理,输出的格式问题。

这里先来总结一下C++中处理括号的几种输入方式:

1. 用char记录

主要是用scanf("%s", cha);然后通过for循环取出除()外的数进行整合。

			char cha[10];
			scanf("%s", cha);
			if (cha[0] == '-'||cha[0] == '0') continue;
			result.number++;
			int j, temp = 0;
			for (j = 0; j < strlen(cha); j++) {
				if (cha[j] == '(') {
					int temp1 = 0;
					for (int k = j + 1; k < strlen(cha) - 1; k++) {
						temp1 = temp1 * 10 + cha[k] - '0';
					}
					result.totalTime += temp1*n;
					break;
				}
				temp = temp * 10 + cha[j] - '0';
			}
			result.totalTime += temp;
2. 用string记录

此时我学习了string的几种将string转为int的方法:

该种方法我测试结果没问题但是在平台上AC不了,不知道是哪的问题,有无明白人士指点?

//stoi和atoi包含在#include<string>,不是c++11的可以在#include<cstring>。
		for (int i = 0; i < m; i++) {
			string tempTimeStr;
			cin >> tempTimeStr;
			if (tempTimeStr[tempTimeStr.length() - 1] == ')') {
				int tempTime0 = stoi(tempTimeStr.substr(0, tempTimeStr.length() - 3));
				int times = stoi(tempTimeStr.substr(tempTimeStr.length() - 2,1));
				//int tempTime0 = atoi(tempTimeStr.substr(0, tempTimeStr.length() - 3).c_str());
				//int times = atoi(tempTimeStr.substr(tempTimeStr.length() - 2,1).c_str());
				result.number++;
				result.totalTime += tempTime0 + n * times;
			}
			else {
				int tempTime1 = stoi(tempTimeStr.substr(0, tempTimeStr.length()));
				//int tempTime1 = atoi(tempTimeStr.substr(0, tempTimeStr.length()).c_str());
				if (tempTime1 > 0) {
					result.number++;
					result.totalTime += tempTime1;
				}
			}
		}
3. 用sscanf处理

这种方法极其精简优秀。

C 库函数 int sscanf(const char *str, const char *format, …),从字符串读取格式化输入。

其API可以参考:C 库函数 - sscanf()
sscanf的部分用法及处理可以参考:sscanf的用法

参考他人的完整解题代码如下:他人精简代码

这里还要注意左对齐右对齐的输入方法:

1. C++中的设置方法:

C++中主要是下面这种,left代表左对齐,10代表占的字符位数。但可能会遇到一些小问题,解决方法可参考:c++中关于设置左对齐和右对齐的问题解决方法

cout << setiosflags(ios::left) << setw(10) << (*it).name << " ";

还有一种。在C++里面,默认是右对齐,可以通过cout.setf(std::ios::left)调整为左对齐,这种调整是全局的,一次设置,后面都有效。参考:C/C++中的输出对齐设置

2. C语言中的设置方法:

C语言表示起来还是方便简洁许多。如下。%-10s代表左对齐,占10个字符位。

printf("%-10s%3d%5d\n",(*it).name,(*it).number,(*it).totalTime);

C++全部代码

#include<iostream>
#include<string>
#include<set> 
#include<cstring>
#include<string.h>
#include<iomanip>
#include<stdio.h>
#include<stdlib.h>
using namespace std;

struct Result
{
	char name[10];
	int number;
	int totalTime;
	
	bool operator < (const Result &y) const
	{
		if (number == y.number) {//strcmp小于返回负数,大于返回正数,等于返回0
		return totalTime == y.totalTime ? strcmp(name, y.name) < 0 : totalTime < y.totalTime;
		}
		else {
			return number > y.number;
		}
	}
};
int main() {
	int m, n;
	cin >> m >> n;
	Result result = { 0 };
	set<Result> sum;
	while (cin>>result.name) {
		result.number = 0;
		result.totalTime = 0;
		char cha[10];
		for (int i = 0; i < m; i++) {
//			string tempTimeStr;
//			cin>>tempTimeStr;
//			if (tempTimeStr[tempTimeStr.length() - 1] == ')') {
//				//int tempTime0 = stoi(tempTimeStr.substr(0, tempTimeStr.length() - 3));
//				//int times = stoi(tempTimeStr.substr(tempTimeStr.length() - 2,1));
//				int tempTime0 = atoi(tempTimeStr.substr(0, tempTimeStr.length() - 3).c_str());
//				int times = atoi(tempTimeStr.substr(tempTimeStr.length() - 2,1).c_str());
//				result.number++;
//				result.totalTime += tempTime0 + n * times;
//			}
//			else {
//				//int tempTime1 = stoi(tempTimeStr.substr(0, tempTimeStr.length()));
//				int tempTime1 = atoi(tempTimeStr.substr(0, tempTimeStr.length()).c_str());
//				if (tempTime1 > 0) {
//					result.number++;
//					result.totalTime += tempTime1;
//				}
//			}
			scanf("%s", cha);
			if (cha[0] == '-'||cha[0] == '0') continue;
			result.number++;
			int j, temp = 0;
			for (j = 0; j < strlen(cha); j++) {
				if (cha[j] == '(') {
					int temp1 = 0;
					for (int k = j + 1; k < strlen(cha) - 1; k++) {
						temp1 = temp1 * 10 + cha[k] - '0';
					}
					result.totalTime += temp1*n;
					break;
				}
				temp = temp * 10 + cha[j] - '0';
			}
			result.totalTime += temp;
		}
		sum.insert(result);
	}
	for (set<Result>::iterator it = sum.begin(); it != sum.end(); it++)//用迭代器遍历整个容器
	{
		printf("%-10s%3d%5d\n",(*it).name,(*it).number,(*it).totalTime);
//		cout << setiosflags(ios::left) << setw(10) << (*it).name << " ";
//		cout << setiosflags(ios::right) << setw(2) << (*it).number << " ";
//		cout << setiosflags(ios::right) << setw(4) << (*it).totalTime << endl;
//		cout << resetiosflags(ios::right);
	}
	system("pause");
	return 0;
}

千辛万苦,终于到了最后一题了。

C - 瑞神打牌 (不支持C++11,G++和C++编译器都试试提交哈)

瑞神HRZ因为疫情在家闲得无聊,同时他又非常厉害,所有的课对他来说都是水一水就能拿A+,所以他无聊,找来了另外三个人:咕咕东,腾神以及zjm来打牌(天下苦瑞神久矣)。
显然,牌局由四个人构成,围成一圈。我们称四个方向为北 东 南 西。对应的英文是North,East,South,West。游戏一共由一副扑克,也就是52张构成。开始,我们指定一位发牌员(东南西北中的一个,用英文首字母标识)开始发牌,发牌顺序为顺时针,发牌员第一个不发自己,而是发他的下一个人(顺时针的下一个人)。这样,每个人都会拿到13张牌。
现在我们定义牌的顺序,首先,花色是(梅花)<(方片)<(黑桃)<(红桃),(输入时,我们用C,D,S,H分别表示梅花,方片,黑桃,红桃,即其单词首字母)。对于牌面的值,我们规定2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < T < J < Q < K < A。
现在你作为上帝,你要从小到大排序每个人手中的牌,并按照给定格式输出。(具体格式见输出描述和样例输出)。

Input

输入包含多组数据
每组数据的第一行包含一个大写字符,表示发牌员是谁。如果该字符为‘#’则表示输入结束。
接下来有两行,每行有52个字符,表示了26张牌,两行加起来一共52张牌。每张牌都由两个字符组成,第一个字符表示花色,第二个字符表示数值。

Output

输出多组数据发牌的结果,每组数据之后需要额外多输出一个空行!!!!!
每组数据应该由24行的组成,输出按照顺时针方向,始终先输出South Player的结果,每位玩家先输出一行即玩家名称(东南西北),接下来五行,第一行和第五行输出固定格式(见样例),第二行和第四行按顺序和格式输出数值(见样例),第三行按顺序和格式输出花色(见样例)。

Sample Input
N
CTCAH8CJD4C6D9SQC7S5HAD2HJH9CKD3H6D6D7H3HQH4C5DKHKS9
SJDTS3S7S4C4CQHTSAH2D8DJSTSKS2H5D5DQDAH7C9S8C8S6C2C3
#
Sample Output
South player:
+---+---+---+---+---+---+---+---+---+---+---+---+---+
|6 6|A A|6 6|J J|5 5|6 6|7 7|9 9|4 4|5 5|7 7|9 9|T T|
| C | C | D | D | S | S | S | S | H | H | H | H | H |
|6 6|A A|6 6|J J|5 5|6 6|7 7|9 9|4 4|5 5|7 7|9 9|T T|
+---+---+---+---+---+---+---+---+---+---+---+---+---+
West player:
+---+---+---+---+---+---+---+---+---+---+---+---+---+
|2 2|5 5|9 9|K K|5 5|7 7|9 9|4 4|T T|J J|A A|8 8|A A|
| C | C | C | C | D | D | D | S | S | S | S | H | H |
|2 2|5 5|9 9|K K|5 5|7 7|9 9|4 4|T T|J J|A A|8 8|A A|
+---+---+---+---+---+---+---+---+---+---+---+---+---+
North player:
+---+---+---+---+---+---+---+---+---+---+---+---+---+
|3 3|4 4|J J|2 2|3 3|T T|Q Q|K K|8 8|Q Q|K K|2 2|3 3|
| C | C | C | D | D | D | D | D | S | S | S | H | H |
|3 3|4 4|J J|2 2|3 3|T T|Q Q|K K|8 8|Q Q|K K|2 2|3 3|
+---+---+---+---+---+---+---+---+---+---+---+---+---+
East player:
+---+---+---+---+---+---+---+---+---+---+---+---+---+
|7 7|8 8|T T|Q Q|4 4|8 8|A A|2 2|3 3|6 6|J J|Q Q|K K|
| C | C | C | C | D | D | D | S | S | H | H | H | H |
|7 7|8 8|T T|Q Q|4 4|8 8|A A|2 2|3 3|6 6|J J|Q Q|K K|
+---+---+---+---+---+---+---+---+---+---+---+---+---+
分析

该题主要注意map的使用(该题C++11不支持,主要初始化方式),"/“和”%"的合理利用即可。bool operator <(const Card &b) const{}也是很常见的一种处理排序的方式了,其中又一次有对三目运算符?:的使用。
不太熟悉map的使用的话可以参考:C++ map用法总结(整理),老师的《C++ 与 STL(下)、搜索(上)》PPT上也有对map使用方法的介绍

C++完整代码

#include<iostream>
#include<map>
#include<cstdio>
#include<algorithm>
using namespace std;

map<char, int>match_sendPeo;
map<char, int> match_color;
map<char, int> match_number;
struct Card
{
	char color;
	char number;

	bool operator <(const Card &b) const{
		return match_color[color] == match_color[b.color] ? 
		match_number[number] < match_number[b.number] : match_color[color] < match_color[b.color];
	}
};
Card sum[4][13];
void initial() {//将map初始化 
	match_sendPeo['S'] = 0; match_sendPeo['W'] = 1; match_sendPeo['N'] = 2; match_sendPeo['E'] = 3;
	match_color['C'] = 0; match_color['D'] = 1; match_color['S'] = 2; match_color['H'] = 3;
	const char num[13] = { '2','3','4','5','6','7','8','9','T','J','Q','K','A' };
	for (int i = 0; i < 13; i++) {
		match_number[num[i]] = i;
	}
}
void printCards(Card* s) {//打印格式需要注意 
	printf("+---+---+---+---+---+---+---+---+---+---+---+---+---+");
	for (int i = 0; i < 13; i++) {
		if (i == 0) {
			printf("\n|%c %c|", s[i].number, s[i].number);
		}
		else {
			printf("%c %c|", s[i].number, s[i].number);
		}
	}
	for (int i = 0; i < 13; i++) {
		if (i == 0) {
			printf("\n| %c |", s[i].color);
		}
		else {
			printf(" %c |", s[i].color);
		}
	}
	for (int i = 0; i < 13; i++) {
		if (i == 0) {
			printf("\n|%c %c|", s[i].number, s[i].number);
		}
		else {
			printf("%c %c|", s[i].number, s[i].number);
		}
	}
	printf("\n+---+---+---+---+---+---+---+---+---+---+---+---+---+\n");
}
void printName(int i) {
	switch (i)
	{case 0:
		cout << "South player:" << endl;
		break;
	case 1:
		cout << "West player:" << endl;
		break;
	case 2:
		cout << "North player:" << endl;
		break;
	case 3:
		cout << "East player:" << endl;
		break;
	default:
		break;
	}
}
int main() {
	char name;
	initial();
	while(~scanf("%c",&name)&&name!='#'){
		getchar();//处理回车 
		int first = match_sendPeo[name] + 1;
		for (int i = 0; i < 52; i++) {
		//注意一下&有无的区别,sum[(i + first) % 4][i / 4];的使用很关键 
			Card& cur = sum[(i + first) % 4][i / 4];
			scanf("%c%c", &(cur.color), &(cur.number));
			if (i == 25) getchar();//处理回车 
		}
		getchar();
		for (int i = 0; i < 4; i++) {
			sort(&sum[i][0], &sum[i][13]);//排序 
			printName(i);
			printCards(sum[i]);
		}
		cout << endl;
	}
	return 0;
}

至此,这周的三个实验的分析到此结束~再接再厉!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值