PAT刷题记录-乙级

前言

记录PAT刷题笔记和代码。算法笔记中例题的记录已经放在《算法刷题笔记》文章中了。以下是PAT题库中算法笔记中未出现的题目,这里均为PAT乙级题目。


目录

前言

乙级

B1051 复数乘法

B1052 卖个萌

B1053 住房空置率

B1054 求平均值

B1055 集体照

B1056 组合数的和

B1057 选择题

B1059 C语言竞赛

B1060 爱丁顿数

B1062 最简分数

B1064 朋友数

B1065 单身狗

B1067 试密码

B1068 万绿丛中一点红

B1069 微博转发抽奖

B1070 结绳

B1071 小赌怡情

B1072 开学寄语

B1073 多选题常见计分法

B1074 宇宙无敌加法器

B1075 链表元素分类 

B1076 Wifi密码

B1077 互评成绩计算

B1078 字符串压缩与解压

B1079 延迟的回文数

B1080 MOOC期终成绩

B1081 检查密码

B1082 射击比赛

B1083 是否存在相等的差

B1084 外观数列

B1085 PAT单位排行

B1086 就不告诉你

B1088 三人行

B1089 狼人杀-简单版

B1090 危险品装箱

B1091 N-自守数

B1092 最好吃的月饼

B1093 字符串A+B

B1094 谷歌的招聘

B1095 解码PAT准考证


乙级

  • B1051 复数乘法

15分题,没思考细节,最后两个样例没过蒙了。

后来发现有几个坑点:

1、浮点数和0比较的方法: 比如精度是0.01,则A + 0.005 >= 0 && A < 0 表示A等于0。

2、即便是0.00+0.00i也要按这样的格式输出。

#include<cstdio> //33-58 = 25
#include<cmath>

int main() {
	double r1, p1, r2, p2, r, p, A, B;
	scanf("%lf%lf%lf%lf", &r1, &p1, &r2, &p2);
	r = r1 * r2;
	p = p1 + p2;
	A = r * cos(p);
	B = r * sin(p);
	if(A + 0.005 >= 0 && A < 0) printf("0.00");
	else printf("%.2f", A);
	if(B + 0.005 >= 0 && B < 0) printf("+0.00i");
	else if(B >= 0) printf("+%.2fi", B);
	else printf("%.2fi", B);
} 

  • B1052 卖个萌

20分,这题坑了我很久,结果发现是"Are you kidding me? @\/@"中的"\"是需要输出。被网友误导了......正常情况下肯定会转义输出反斜杠的,我看了网上的就一直以为不用输出。属实是犯傻了,看来还是得自己思考和判断。

另外注意下标是从1开始的。还有给出的字符中存在非ASCII码,最好用string存储符号(C串没试过,单个char数组应该要开的大一点)。由于不知道有多少个符号,因此用vector<string>存储符号。

一点感想:找不到bug的时候容易怀疑哪个知识点不对,搞得下次遇到类似问题都不确定了。还是要验证清楚的好。

读取一行字符串,从中获取[]内的符号串的方法:

方法1:

void change(string str, vector<string>& v) {
	for(int i = 0; i < str.length(); i ++) {
		if(str[i] == '[') {
			int k = str.find(']');
			v.push_back(str.substr(i + 1, k - i - 1));
			str.erase(k, 1);
		}
	}
}

一点注释:str.length()在str.erase()操作后其实是实时变化的。对应的下标i就会相应发生变化,由于这里不涉及在原字符串上的进一步统计或修改,只是把需要的字符串push到vector中了。

方法2 :

void change(string str, vector<string>& v) {
	int i = 0, j = 0;
	while(i < str.length()) {
		if(str[i] == '[') {
			while(j ++ < str.length()) {
				if(str[j] == ']') {
					v.push_back(str.substr(i + 1, j - i - 1));
					break;
				}
			}
		}
		i ++;
	}
}

AC代码:

#include<cstdio>
#include<iostream>
#include<string>
#include<vector>
using namespace std;


void change(string str, vector<string>& v) {
	for(int i = 0; i < str.length(); i ++) {
		if(str[i] == '[') {
			int k = str.find(']');
			v.push_back(str.substr(i + 1, k - i - 1));
			str.erase(k, 1);
		}
	}
}


int main() {
	int k;
	char ch;
	vector<string> hand, eye, mouth;
	string str;
	getline(cin, str);
	change(str, hand);
	getline(cin, str);
	change(str, eye);
	getline(cin, str);
	change(str, mouth);
	int h = hand.size();
	int e = eye.size();
	int m = mouth.size();

	scanf("%d", &k);
	int lh, le, mm, re, rh;
	for(int i = 0; i < k; i ++) {
		scanf("%d %d %d %d %d", &lh, &le, &mm, &re, &rh);
		if(lh < 1 || lh > h || le < 1 || le > e || mm < 1 || mm > m || re < 1 || re > e || rh < 1 || rh > h)
			printf("Are you kidding me? @\\/@\n");
		else
			printf("%s(%s%s%s)%s\n", hand[lh - 1].c_str(), eye[le - 1].c_str(), mouth[mm - 1].c_str(), eye[re - 1].c_str(), hand[rh - 1].c_str());
	}
	return 0;
}

  • B1053 住房空置率

样例2和3始终过不了,检查了好几遍代码没发现错误。结果网上一搜发现是题目读错了是观察期k超过D,不是低电量天数cnt超过D,......,心态崩了。遇到不可理解的错误时,一定要仔细审题,重新读几遍题目,血的教训。

检查错误的时候还以为浮点数比较的问题。查阅资料得到的结论是:两个浮点数比较> <可以直接比较,本例中也可以,== 不能直接比较。其实> < 也最好不要直接比较,浮点数比较的正确写法如下:

const double eps = 1e-8;
#define Equ(a, b) ((fabs((a) - (b))) < (eps)) // ==
#define More(a, b) (((a) - (b)) > (eps)) // >
#define Less(a, b) (((a) - (b)) < (-eps)) // <
#define MoreEqu(a, b) (((a) - (b)) > (-eps)) // >=
#define LessEqu(a, b) (((a) - (b)) < (eps)) // >=

AC代码:

#include<cstdio> 

int main() {
	int n, D, k;
	double e, temp;
	scanf("%d %lf %d", &n, &e, &D);
	int cntEmpty = 0, cntMayEmpty = 0;
	for(int i = 0; i < n; i ++) {
		scanf("%d", &k);
		int cnt = 0;
		for(int j = 0; j < k; j ++) {
			scanf("%lf", &temp);
			if(temp < e) cnt ++;
		}
		if(cnt > k / 2) {
			if(k > D) cntEmpty ++;
			else cntMayEmpty ++;
		}
	}
	
	printf("%.1f%% %.1f%%", (double)cntMayEmpty / n * 100, (double)cntEmpty / n * 100);
	return 0;
}
  • B1054 求平均值

关键是判断字符是否是合法数字,主要有以下几点:

1、得到小数点下标后,后面的数字超过2个则不合法(精度要求) 

2、统计小数点个数,超过1个则不合法

3、第一个字符为“-”时,后面再出现“-”则不合法

4、字符不是数字且不是“-”且不是“.”则不合法

通过以上判断的字符串是数字,接下来用stod()将字符串转double,判断范围,>1000或<-1000则不合法。

最后输出时根据合法数字个数输出结果,0和1需特判。

笔记:string中数值类型和string类型的相互转换:

数值转string:

to_string()

string转数值:

stoi(s,p,b):将string类型转换为int类型;

stol(s.p,b):将string类型转换为long类型;

stoul(s,p,b):将string类型转换为unsigned long类型;

stoll(s,p,b):将string类型转换为long long类型;

stoul(s,p,b):将string;类型转换为unsigned long long类型;

stof(s,p):将string类型转换为float类型;

stod(s,p):将string类型转换为double类型;

stold(s,p):将string类型转换为long double类型。

注:b表示转换所用的基数,默认值为10,p是size_t指针,用来保存s中第一个非数值字符的下标,p默认为0,即不保存下标。
 

代码:

#include<cstdio>
#include<iostream>
#include<string>
#include<cctype>
using namespace std;

bool isLegal(string num) {
	int cntDot = 0, cntNum = 0, dotIdx = 0; 
	for(int i = 0; i < num.length(); i ++) {
		if(dotIdx != 0) cntNum ++;
		if(cntNum > 2) return false;
		if(num[i] == '.') {
			cntDot ++;
			dotIdx = i; 
		}
		if(cntDot > 1) return false;
		if(num[0] == '-' && i > 0 && num[i] == '-') return false;
		if(!isdigit(num[i]) && num[i] != '-' && num[i] != '.')
			return false;
	}
	if(stod(num) > 1000.0 || stod(num) < -1000.0)
		return false;
	return true;
}

int main() {
	int n;
	scanf("%d", &n);
	string number;
	double sum = 0.0;
	int cnt = 0;
	for(int i = 0; i < n; i ++) {
		cin >> number;		
		if(isLegal(number)) {
			cnt ++;
			sum += stod(number);
		}
		else
			printf("ERROR: %s is not a legal number\n", number.c_str());
	}
	if(cnt == 0) printf("The average of 0 numbers is Undefined");
	else if(cnt == 1) printf("The average of 1 number is %.2f", sum);
	else {
		double avg = sum / cnt;
		printf("The average of %d numbers is %.2f", cnt, avg);
	}
	return 0;
} 

  • B1055 集体照

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<deque>
using namespace std;

const int maxn = 10010;
struct Student {
	char name[10];
	int height;
} stu[maxn];

bool cmp(Student a, Student b) {
	if(a.height != b.height)
		return a.height > b.height;
	else
		return strcmp(a.name, b.name) < 0;
}

int main() {
	int n, k;
	scanf("%d %d", &n, &k);
	for(int i = 0; i < n; i ++) {
		scanf("%s %d", stu[i].name, &stu[i].height);
	}
	sort(stu, stu + n, cmp);
	int perRow = n / k, index = 0;
	deque<Student> dq;
	for(int i = 0; i < k; i ++) {
		if(i == 0) {
			int j = 0;
			while(true) {
				if(j ++ >= n - perRow * (k - 1)) break;
				dq.push_back(stu[index ++]);
				if(j ++ >= n - perRow * (k - 1)) break;
				dq.push_front(stu[index ++]);
			}
			for(int i = 0; i < dq.size(); i ++) {
				printf("%s", dq[i].name);
				if(i < dq.size() - 1) printf(" ");
				else printf("\n");
			}
			dq.clear(); 
		}
		else {
			int j = 0;
			while(true) {
				if(j ++ >= perRow) break;
				dq.push_back(stu[index ++]);
				if(j ++ >= perRow) break;
				dq.push_front(stu[index ++]);
			}
			for(int i = 0; i < dq.size(); i ++) {
				printf("%s", dq[i].name);
				if(i < dq.size() - 1) printf(" ");
				else printf("\n");
			}
			dq.clear();
		}
	}
	return 0;
}
  • B1056 组合数的和

#include<cstdio>

int a[11];

int main() {
	int n;
	scanf("%d", &n);
	for(int i = 0; i < n; i ++) {
		scanf("%d", &a[i]);
	}
	int sum = 0;
	for(int i = 0; i < n; i ++) {
		for(int j = 0; j < n; j ++) {
			if(j == i) continue;
			sum += a[i] * 10 + a[j];
		}
	}
	printf("%d", sum);
	return 0;
}

  • B1057 选择题

练习时浪费时间就是为考试时节省时间(自我安慰.jpg)

这题卡在scanf()的读入上了。涉及%c的读入有许多注意点:比如%c可以读空格。

笔记:scanf()的学习

%c可以读空格,因此想要变量中不含空格需要主动读掉空格,如scanf("%c %c %c")

由于%c可以读换行符,因此可能使得本次的scanf读取了上面语句的换行符,而导致错误。

一个好方法:scanf(" %c", &c); %c前的空格告诉scanf忽略前面的空白字符。

而%d读取的时候可以自动忽略空白字符。

#include<cstdio> 
#include<vector>
using namespace std; 

struct Problem {
	int full;
	int falseCount;
	vector<char> trueChoice;
} prob[110]; 

int main() {
	int n, m, choiceNum, trueNum;
	char ans;
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= m; i ++) {
		scanf("%d %d %d", &prob[i].full, &choiceNum, &trueNum);
		for(int j = 1; j <= trueNum; j ++) {
			scanf(" %c", &ans); //忽略前面的空白字符
//			printf("read = %c\n", ans);
			prob[i].trueChoice.push_back(ans);
		}
		prob[i].falseCount = 0;
	}
	int num;
	vector<char> stuAns;
	bool flag = false;
	for(int i = 0; i < n; i ++) {
		scanf("\n");//吃掉之前的换行符 
		int sum = 0;
		for(int j = 1; j <= m; j ++) {
			if(j != 1) scanf(" ");
			scanf("(%d", &num); //吃掉(
//			printf("num = %d\n", num);
			for(int k = 0; k < num; k ++) {
				scanf(" %c", &ans); //忽略前面的空白字符
//				printf("ans = %c\n", ans);
				stuAns.push_back(ans);
			}
			scanf(")"); //吃掉)
			if(stuAns == prob[j].trueChoice) {
				sum += prob[j].full;
			}
			else {
				prob[j].falseCount ++;
				flag = true;
			}
			stuAns.clear();
		}
		printf("%d\n", sum);
	}
	int maxError = 0;
	for(int i = 1; i <= m; i ++) {
		if(prob[i].falseCount > maxError)
			maxError = prob[i].falseCount;
	}
	if(maxError == 0) printf("Too simple\n");
	else {
		printf("%d", maxError);
		for(int i = 1; i <= m; i ++) {
			if(prob[i].falseCount == maxError)
				printf(" %d", i);
		}
	}
	
	return 0;
}
  • B1059 C语言竞赛

做题要有耐心,debug也要耐心,仔细检查代码,可能是很小的错误避免影响心态。(自我安慰.jpg)

本题涉及素数的判断。设一个int数组map,表示id->prize(index)的映射,prize数组存储字符串。

#include<cstdio>

const int maxn = 10010;
int map[maxn] = {0}; // id->prize
char prize[3][20] = {"Mystery Award", "Minion", "Chocolate"};
bool checked[maxn] = {0};

bool isPrime(int n) {
	if(n <= 1) return false;
	for(int i = 2; i * i <= n; i ++)
		if(n % i == 0) return false;
	return true;
}  

int main() {
	int n, id, k;
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) {
		scanf("%d", &id);
		if(i == 1) map[id] = 1;
		else if(isPrime(i)) map[id] = 2;
		else map[id] = 3;
	} 
	scanf("%d", &k);
	for(int i = 0; i < k; i ++) {
		scanf("%d", &id);
		if(map[id] == 0) printf("%04d: Are you kidding?\n", id);
		else {
			if(checked[id] == false) {
				printf("%04d: %s\n", id, prize[map[id] - 1]);
				checked[id] ++;	
			}
			else printf("%04d: Checked\n", id);
		}
	}
	
	return 0;
} 

  • B1060 爱丁顿数

一开始想复杂了,在读取每个数字后把所有小于这个数的计数器+1,结果有一个样例超时。后来仔细想一下,要想求E天超过E千米的最大值,且题目中每天路程大学的顺序没有要求,因此可以把每天的路程从大到小排序,这时候从左到右就是,如果dis[i] > i就说明连续i天超过i千米,因为比下标i小都保证超过i千米,这样找到最大i即可。当第一个不满足dis[i] > i的时候,i - 1就是最大的E。一点需要注意的是当所有i都满足dis[i] > i时,i == n + 1,E = n。

#include<cstdio> 
#include<algorithm>
using namespace std;
const int maxn = 100010;
int dis[maxn];

bool cmp(int a, int b) {
	return a > b;
}

int main() {
	int n;
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) {
		scanf("%d", &dis[i]);
	}
	sort(dis + 1, dis + n + 1, cmp);
	
	int i;
	for(i = 1; i <= n; i ++) { 
		if(dis[i] <= i) {
			printf("%d", i - 1);
			break;
		}
	}
	if(i == n + 1)  printf("%d", n);
	
	return 0;
}

  • B1062 最简分数

这题只能说我思维定势了。。一开始疯狂通分求gcd,lcm,最后模拟出来只过了样例,但答案是错的,一直没找到错误。后来网上看到一个很直接的解法,直接把分数当浮点数算就行了。

最简分数指的是gcd(n, m)为1的分数。

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

int gcd(int a, int b) {
	if(b == 0) return a;
	else return gcd(b, a % b);
}

int main() {
	int n1, m1, n2, m2, k;
	scanf("%d/%d %d/%d %d", &n1, &m1, &n2, &m2, &k);
	if(n1 * m2 > n2 * m1) {
		swap(n1, n2);
		swap(m1, m2);
	}
	int cnt = 0;
	for(int i = 1; i < k; i ++) {
		if(gcd(i, k) == 1) {
			if(1.0 * i / k > 1.0 * n1 / m1 && 1.0 * i / k < 1.0 * n2 / m2) {
				if(cnt ++) printf(" ");
				printf("%d/%d", i, k);
			}
		}
	}
	return 0;
}
  • B1064 朋友数

string存储数字遍历求和比较方便,哈希表统计和出现的次数。

#include<cstdio>
#include<iostream>
#include<string>
using namespace std;

int hashTable[40] = {0};

int main() {
	int n;
	string num;
	scanf("%d", &n);
	for(int i = 0; i < n; i ++) {
		cin >> num;
		int sum = 0;
		for(int i = 0; i < num.length(); i ++) {
			sum += num[i] - '0';
		}
		hashTable[sum] ++;
	}
	int cnt = 0;
	for(int i = 0; i < 40; i ++) {
		if(hashTable[i] != 0) cnt ++;
	}
	printf("%d\n", cnt);
	int cnt0 = 0;
	for(int i = 0; i < 40; i ++) {
		if(hashTable[i] != 0) {
			if(cnt0 ++) printf(" ");
			printf("%d", i); 
		}
	}
	return 0;
} 

  • B1065 单身狗

题意很简单,但一开始思路比较混合,用的方法最后2个样例超时。最后采用的方法:用一个map存储相互映射关系,可以将输入存到数组a中(一开始没有存储下来,用集合删除一顿操作,导致复杂度太高超时),然后再次遍历找map[a[i]]是否在party集合中,不在的插入一个新的集合single中即可。

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

const int maxn = 50010;
int a[maxn];

int main() {
	int n, m, id0, id1;
	set<int> party, single;
	map<int, int> mp;
	scanf("%d", &n);
	for(int i = 0; i < n; i ++) {
		scanf("%d %d", &id0, &id1);
		mp[id0] = id1;
		mp[id1] = id0;
	}
	scanf("%d", &m);
	for(int i = 0; i < m; i ++) {
		scanf("%d", &a[i]);
		party.insert(a[i]);
	}
	for(int i = 0; i < m; i ++) {
		int A = mp[a[i]];
		if(party.find(A) == party.end()) {
			single.insert(a[i]);
		}
	}
	
	printf("%d\n", single.size());
	for(auto it = single.begin(); it != single.end(); it ++) {
		if(it != single.begin()) printf(" "); 
		printf("%05d", *it);
	}
	return 0;
} 
  • B1067 试密码

这题很迷,首先最大的坑点竟然是个文字游戏:第一行给出一个密码(长度不超过 20 的、不包含空格、Tab、回车的非空字符串)。随后每行给出一个以回车结束的非空字符串,是用户尝试输入的密码。

意思是:前面的密码没有空格,后面的非空字符串中可能包含空格。。。所以后面不能用cin要用getline()

第二个坑点:如果单纯按照题目描述的逻辑进行判断的话,会有一个样例过不了(所以感觉还是题目描述不太到位)。最后的逻辑是输入的次数不超过n且密码正确输出welcome in,输入的次数不超过n且密码错误输出wrong password,如果密码错误且输入次数到达n,则输出account locked。

发现写完代码后判断逻辑就清晰了。

#include<cstdio>
#include<iostream>
#include<string>
using namespace std;

int main() {
	string passwd, str;
	int n;
	cin >> passwd >> n;
	int cnt = 0;
	getchar();
	while(getline(cin, str) && str != "#") {
		cnt ++;
		if(cnt <= n && str == passwd) {
			printf("Welcome in\n");
			break;
		}
		else if(cnt <= n && str != passwd) {
			printf("Wrong password: %s\n", str.c_str());
			if(cnt == n) {
				printf("Account locked\n");
				break;
			}
		}
	}
	return 0;
}
  • B1068 万绿丛中一点红

一个坑点:是M列N行,而不是M行N列。最后输出的是先列号后行号。

此外在像素值只出现一次的情况下,记录差值是否大于tol,只需记录a[i][j]和周围8个的最小差值MIN,如果MIN > tol则是独一无二的像素。

#include<cstdio>
#include<algorithm>

using namespace std;
const int N = 1010;
const int maxn = 100000000;
int a[N][N] = {0};
int hashTable[maxn] = {0};

int main() {
	int m, n, tol;
	scanf("%d %d %d", &m, &n, &tol);
	for(int i = 1; i <= n; i ++) { //N行(6) 
		for(int j = 1; j <= m; j ++) { //M列(8) 
			scanf("%d", &a[i][j]); // a[行标][列标] 6*8
			hashTable[a[i][j]] ++;
		}
	}
	int cnt = 0, x, y;
	for(int i = 1; i <= n; i ++) {
		for(int j = 1; j <= m; j ++) {	
			if(hashTable[a[i][j]] == 1) {
				int MIN = maxn;
				for(int s = -1; s <= 1; s ++) {
					for(int t = -1; t <= 1; t ++) {
						if(s == 0 && t == 0) continue;
						if(abs(a[i + s][j + t] - a[i][j]) < MIN)
							MIN = abs(a[i + s][j + t] - a[i][j]);
					}
				}
				if(MIN > tol) {
					cnt ++;
					x = i; //行标 
					y = j; //列标
				}
			}
		}
	}
	if(cnt == 0) printf("Not Exist\n");
	else if(cnt > 1) printf("Not Unique\n");
	else printf("(%d, %d): %d\n", y, x, a[x][y]);
	return 0;
}
  • B1069 微博转发抽奖

用一个vector存储转发的id字符串,然后从s - 1开始遍历,步长为n,如果字符串在map中未出现过,则输出,并在map中记录,并记录有人获奖flag=true;如果在map出现过则一个个遍历找到下一个没出现的id,输出并记录获奖flag=true。

注意最后判断keep going的条件不是i >= list.size(),因为没人获奖也会超过,因此用一个flag记录。

#include<cstdio>
#include<iostream>
#include<string>
#include<vector>
#include<map>
using namespace std;

int main() {
	int m, n, s;
	string id;
	vector<string> list;
	map<string, int> mp;
	scanf("%d %d %d", &m, &n, &s);
	for(int i = 0; i < m; i ++) {
		cin >> id;
		list.push_back(id); 
	}
	bool flag = false;
	for(int i = s - 1; i < list.size(); i += n) {
		if(mp[list[i]] == 0) {
			cout << list[i] << endl;
			mp[list[i]] = 1;
			flag = true;
		}
		else {
			while(mp[list[i]] != 0)
				i ++;
			cout << list[i] << endl;
			flag = true;
		}
	}
	if(flag == false) cout << "Keep going..." << endl; 
	return 0;
} 
  • B1070 结绳

类似于求哈夫曼树,每次选择最短的两段绳子结合,结合后的长度位两段绳子长度之和的一半,反复进行直至只有一段绳子即为最长。用优先队列(小堆顶)实现。

#include<cstdio>
#include<queue>
using namespace std;

//小堆顶 
priority_queue<double, vector<double>, greater<double>> q;

int main() {
	int n;
	double s, x, y;
	scanf("%d", &n);
	for(int i = 0; i < n; i ++) {
		scanf("%lf", &s);
		q.push(s);
	}
	while(q.size() > 1) {
		x = q.top();
		q.pop();
		y = q.top();
		q.pop();
		q.push((x + y) / 2);
	} 
	printf("%d", (int)q.top());
	return 0;
}
  • B1071 小赌怡情

注意标点符号(!和.)以及两个空格。

#include<cstdio>

int main() {
	int T, K, n1, b, t,n2;
	scanf("%d %d", &T, &K);
	for(int i = 0; i < K; i ++) {
		scanf("%d %d %d %d", &n1, &b, &t, &n2);
		if(t > T) {
			printf("Not enough tokens.  Total = %d.\n", T);
			continue;
		}
		if(n1 > n2 && b == 0 || n1 < n2 && b == 1) {
			T += t;
			printf("Win %d!  Total = %d.\n", t, T);
		}
		else {
			T -= t;
			printf("Lose %d.  Total = %d.\n", t, T);
		}
		if(T <= 0) {
			printf("Game Over.\n");
			break;
		}
	}
	return 0;
} 
  • B1072 开学寄语

注意点:四位编号。没有违禁物品的不输出(无换行)

#include<cstdio>

const int maxn = 10010;
int hashTable[maxn] = {0};

int main() {
	int n, m, k, id;
	scanf("%d %d", &n, &m);
	for(int i = 0; i < m; i ++) {
		scanf("%d", &id);
		hashTable[id] = 1;
	}
	char name[5];
	int cntThing = 0, cntStu = 0;
	for(int i = 0; i < n; i ++) {
		scanf("%s %d", name, &k);
		int cnt = 0;
		for(int j = 0; j < k; j ++) {
			scanf("%d", &id);
			if(hashTable[id] == 1) {
				if(cnt ++ == 0) printf("%s:", name);
				printf(" %04d", id);
			}
		}
		if(cnt > 0) {
			printf("\n");
			cntThing += cnt;
			cntStu ++;
		}
	}
	printf("%d %d", cntStu, cntThing); 
	return 0;
}
  • B1073 多选题常见计分法

此题mark,只对了前2个样例,分数处理的太复杂,而且也读没太读懂样例。看了柳神的题解大概明白了,首先错选漏选都算错,但是错选时正确选项不算错。可以这样描述:该选的没选算错,不该选的选了算错。所以要判断对错要把学生的选项存成一个表,和正确选择表一一比对,而不是用集合或数组查找有没有正确选项出现或有没有错误选项出现。

思路解析:用二进制存储答案表,每题最多5个选项。例如abcde全选就表示为11111, 如果学生选了abde就表示未11011。实现上用一个hash数组实现二进制:hash[] = {1, 2, 4, 8, 16}。 hash['a' - 'a'] = hash[0] = 1, hash['b' - 'a'] = hash[1] = 2, hash['e' - 'a'] = hash[4] = 16,分别为二进制的各个位。

如此在统计错误情况时,就可以用异或实现,相异为1,即为错误(假设得到的结果为错误列表el)。如果el == 0那么表示完全正确。不为0表示错误。

在错误的情况下,继续判断是否半对。将选项列表opt和正确选项列表trueOpt相或如果等于trueOpt表示半对(相或可视为不带进位的加,等于trueOpt即表示没有选到错误的选项)。

count数组用来统计错误情况,我们已经知道现在el中为1的位表示错误选项,现在和hash数组(高低位不同的一个1)遍历相与,结果为1表示该位错误。

#include<iostream>
#include<vector>
#include<cmath>
using namespace std;

int main() {
	int n, m, optNum, trueNum, temp, maxCnt = 0;
	int hash[] = {1, 2, 4, 8, 16}, opt[1010][110] = {0};
	char c;
	scanf("%d %d", &n, &m);
	vector<int> full(m), trueOpt(m);
	vector<vector<int>> count(m, vector<int>(5));
	for(int i = 0; i < m; i ++) {
		scanf("%d %d %d", &full[i], &optNum, &trueNum);
		for(int j = 0; j < trueNum; j ++) {
			scanf(" %c", &c);
			trueOpt[i] += hash[c - 'a']; 
		}
	}
	for(int i = 0; i < n; i ++) {
		double grade = 0;
		for(int j = 0; j < m; j ++) {
			getchar();
			scanf("(%d", &temp);
			for(int k = 0; k < temp; k ++) {
				scanf(" %c)", &c);
				opt[i][j] += hash[c - 'a'];
			}
			int el = opt[i][j] ^ trueOpt[j];
			if(el) {
				if((opt[i][j] | trueOpt[j]) == trueOpt[j]) {
					grade += full[j] * 1.0 / 2;
				}
				if(el) {
					for(int k = 0; k < 5; k ++)
						if(el & hash[k]) count[j][k] ++;
				}
			}
			else {
				grade += full[j];
			}
		}
		printf("%.1f\n", grade);
	}
	for(int i = 0; i < m; i ++)
		for(int j = 0; j < 5; j ++)
			maxCnt = maxCnt > count[i][j] ? maxCnt : count[i][j];
	
	if(maxCnt == 0) {
		printf("Too simple\n");
	}
	else {
		for(int i = 0; i < m; i ++) {
			for(int j = 0; j < count[i].size(); j ++) {
				if(maxCnt == count[i][j])
					printf("%d %d-%c\n", maxCnt, i + 1, 'a' + j);
			}
		}
	}
	return 0;
} 
  • B1074 宇宙无敌加法器

用string读取数据,根据进制表的长度len将a和b补充前导0至长度为len;然后遍历从len - 1到0遍历求和,注意设置一个进位,进位为c / 进制mod,结果位上插入c % mod。注意最后一个进位要直接插入结果字符串中。得到结果串后除去前导0,但要注意结果刚好为0则直接输出0。

#include<cstdio>
#include<iostream>
#include<string>
using namespace std;

int main() {
	string table, a, b, ans;
	cin >> table >> a >> b;
	int len = table.length();
	while(a.length() < len) 
		a.insert(0, "0");
	while(b.length() < len)
		b.insert(0, "0");
	int carry = 0;
	for(int i = len - 1; i >= 0; i --) {
		int c = a[i] - '0' + b[i] - '0' + carry;
		int mod = table[i] - '0' ? table[i] - '0' : 10;
		carry = c / mod;
		ans.insert(0, to_string(c % mod));
	}
	if(carry > 0) ans.insert(0, to_string(carry));
	while(ans != "0" && ans[0] == '0')
		ans.erase(0, 1);
	printf("%s", ans.c_str());
	return 0;
}
  • B1075 链表元素分类 

考察数据结构链表的静态写法。建立结构体Node,包括address,data,next外,还包含用于排序的属性:

(1)order:用于node数组按链表顺序排序,初始化为maxn,之后遍历链表,没遍历到的节点会排在最后。

(2)lt0:用于node数组按负数在前排序,lt0 = 0表示负数,lt0 = 1表示正数。初始化为1。

(2)lek:用于node数组按小于k的在前排序,lek = 0表示小于等于k,lek = 1表示大于k。初始化为1。

node排序规则如下:

bool cmp(Node a, Node b) {
	if(a.lt0 != b.lt0)
		return a.lt0 < b.lt0;
	else if(a.lek != b.lek)	
		return a.lek < b.lek; 
	else 
		return a.order < b.order;
}

这样输入完数据后,遍历链表时完整对应变量属性的赋值,最后排序即可。

注意:数据输入时是给node[addr]赋值,而不是给node[i]赋值。

完整代码:

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

const int maxn = 100010;
struct Node {
	int addr, data, next;
	int order; 
	int lt0; //0: <0
	int lek; //0: <=k
} node[maxn];


bool cmp(Node a, Node b) {
	if(a.lt0 != b.lt0)
		return a.lt0 < b.lt0;
	else if(a.lek != b.lek)	
		return a.lek < b.lek; 
	else 
		return a.order < b.order;
}


int main() {
	for(int i = 0; i < maxn; i ++) {
		node[i].lt0 = 1;
		node[i].lek = 1;
		node[i].order = maxn;
	}	
	int start, n, k, addr;
	scanf("%d %d %d", &start, &n, &k);
	for(int i = 0; i < n; i ++) {
		scanf("%d", &addr);
		node[addr].addr = addr;
		scanf("%d %d",&node[addr].data, &node[addr].next);
	}
	
	int p = start, cnt = 0;
	while(p != -1) {
		node[p].order = cnt ++;
		if(node[p].data < 0) node[p].lt0 = 0;
		if(node[p].data <= k) node[p].lek = 0;
		p = node[p].next;
	}
	sort(node, node + maxn, cmp);
	for(int i = 0; i < cnt; i ++) {
		if(i < cnt - 1)
			printf("%05d %d %05d\n", node[i].addr, node[i].data, node[i + 1].addr);
		else
			printf("%05d %d -1\n", node[i].addr, node[i].data);
	}
	return 0;
}
  • B1076 Wifi密码

#include<cstdio>

int main() {
	int N;
	scanf("%d", &N);
	char str[4];
	for(int i = 0; i < N; i ++) {
		for(int j = 0; j < 4; j ++) {
			scanf("%s", str);
			if(str[2] == 'T') printf("%d", str[0] - 'A' + 1);
		}
	}
	return 0;
}
  • B1077 互评成绩计算

遇到一个小坑,学到一个新的知识点:在printf()函数里面使用round()或rand()等函数会输出0,原因是printf本身并不进行数据的类型转换。

#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 110;
int score[maxn];

int main() {
	int N, M, temp;
	double g1, g2;
	scanf("%d %d", &N, &M);
	for(int i = 0; i < N; i ++) {
		int cnt = 0, sum = 0, MIN = maxn, MAX = -1;
		scanf("%lf", &g2);
		for(int j = 1; j < N; j ++) {
			scanf("%d", &temp);
			if(temp >= 0 && temp <= M) {
				if(temp > MAX) MAX = temp;
				if(temp < MIN) MIN = temp;
				cnt ++;
				sum += temp; 
			}
		}
		sum -= MAX;
		sum -= MIN;
		g1 = (double)sum / (cnt - 2);
		int score = round((g1 + g2) / 2);
		printf("%d\n", score);
	}
	return 0;
}
  • B1078 字符串压缩与解压

这题我遇到了一个大坑发现样例2,3死活过不了,终于找到原因了。

读取一行字符串前,需要先读取一个字符,我本来想用scanf("%c\n");来吞掉后面的换行符的,但是发现这样写样例2和3过不了,其他的可以过。可能2和3字符后面不是换行符?无论如何,使用这种方法试图吞掉后面的换行符都不是一个好的方法。还是用getchar比较保险。

最好的正确方法是:scanf("%c"); 然后getchar(); 这样是可以过的。

或者直接用cin:cin >> ch; 然后getchar(); 这样也可以过。

AC代码:

#include<iostream>
#include<string>
#include<cctype>
#include<cstdio>
using namespace std;

// void compress(string str) {
// 	int cnt = 1;
// 	char pre = str[0];
// 	for(int i = 1; i < str.length(); i ++) {
// 		if(str[i] == pre)
// 			cnt ++;
// 		else {
// 			if(cnt >= 2) cout << cnt;
// 			cout << pre;
// 			cnt = 1;
// 			pre = str[i];
// 		}
// 	}
// 	if(cnt >= 2) cout << cnt;
// 	cout << pre;
// }

void compress(string str) {
	string ans;
	int j;
	for(int i = 0; i < str.length(); i = j) {
		for(j = i; j < str.length() && str[j] == str[i]; j ++);
		if(j - i == 1) ans += str[i];
		else ans += to_string(j - i) + str[i];
	}
	cout << ans << endl;
}

void decompress(string str) {
	string num;
	int cnt = 1;
	for(int i = 0; i < str.length(); i ++) {
		if(isdigit(str[i]))
			num += str[i];
		else {
			if(num.length() > 0) cnt = stoi(num);
			while(cnt --) cout << str[i];
			cnt = 1;
			num = ""; 
		}
	} 
}

int main() {
	char ch;
	string str;
	scanf("%c", &ch);
//     cin >> ch;
    getchar();
	getline(cin, str);
	if(ch == 'D') decompress(str);
	else compress(str);
	return 0;
}

  • B1079 延迟的回文数

大整数的相加,回文数的判断。直接用string完成大整数相加,相加前先将字符串逆转。

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

bool isPalindromic(string a) {
	int len = a.length();
	for(int i = 0; i < len / 2; i ++)
		if(a[i] != a[len - i - 1])
			return false;
	return true;
}

string add(string a, string b) {
	string c;
	reverse(a.begin(), a.end());
	reverse(b.begin(), b.end());	
	int carry = 0;
	for(int i = 0; i < a.length() || i < b.length(); i ++) {
		int temp = a[i] - '0' + b[i] - '0' + carry;
		c.insert(0, to_string(temp % 10));
		carry = temp / 10;
	}
	if(carry != 0) c.insert(0, to_string(carry));
	return c;
}

int main() {
	string a, b, c;
	cin >> a;
	int iter = 0;
	while(iter ++ < 10) {
		if(isPalindromic(a)) {
			cout << a << " is a palindromic number." << endl;
			break;
		}
		else {
			b = a;
			cout << a << " + "; 
			reverse(b.begin(), b.end());
			cout << b << " = ";
			c = add(a, b);
			cout << c << endl;
			a = c;
		}
	}
	if(iter > 10) cout << "Not found in 10 iterations." << endl;
	return 0;
} 
  • B1080 MOOC期终成绩

此题Mark。代码写的搅乱,最后一个样例每过。参考了柳神的代码。猜测可能是因为可能存在没有编程成绩,但有后面的成绩的学生存在导致的。这里的idx处理不当。

大体思路:设置一个结构体node表示学生,用vector作为数组。将编程分数大于200的存入数组,记录下标(需要从1开始,因为0需要用于判断是否存在);然后读取Gm,当idx[id] != 0时才记录;然后读取Gf,当idx[id] != 0时才记录,并更新总分G。最后将所有总分>=60的学生放进一个新的答案数组,对答案数组排序,然后输出。

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

struct node {
	string name;
	int gp, gm, gf, g;
};

bool cmp(node a, node b) {
	return a.g != b.g ? a.g > b.g : a.name < b.name;
}
map<string, int> idx;

int main() {
	int p, m, n, score, cnt = 1;
	cin >> p >> m >> n;
	vector<node> v, ans;
	string id;
	for(int i = 0; i < p; i ++) {
		cin >> id >> score;
		if(score >= 200) {
			v.push_back(node{id, score, -1, -1, 0});
			idx[id] = cnt ++;
		} 
	}
	for(int i = 0; i < m; i ++) {
		cin >> id >> score;
		if(idx[id] != 0) v[idx[id] - 1].gm = score;
	}
	for(int i = 0; i < n; i ++) {
		cin >> id >> score;
		if(idx[id] != 0) {
			int temp = idx[id] - 1;
			v[temp].gf = v[temp].g = score;
			if(v[temp].gm > v[temp].gf) v[temp].g = int(v[temp].gm * 0.4 + v[temp].gf * 0.6 + 0.5);
		}
	}
	for(int i = 0; i < v.size(); i ++)
		if(v[i].g >= 60) ans.push_back(v[i]);
	sort(ans.begin(), ans.end(), cmp);
	for(int i = 0; i < ans.size(); i ++)
		printf("%s %d %d %d %d\n", ans[i].name.c_str(), ans[i].gp, ans[i].gm, ans[i].gf, ans[i].g);		
	return 0;	
}
  • B1081 检查密码

注意判断路径是需要数字或需要字母,而不是没有字母和没有数字。

#include<cstdio>
#include<iostream>
#include<string>
#include<cctype>
using namespace std;

bool judge(string str) {
	for(int i = 0; i < str.length(); i ++)
		if(!isalnum(str[i]) && str[i] != '.')
			return false;
	return true;
}

bool needAlpha(string str) {
	for(int i = 0; i < str.length(); i ++) 
		if(isalpha(str[i]))
			return false;
	return true;
} 

bool needNumber(string str) {
	for(int i = 0; i < str.length(); i ++) 
		if(isdigit(str[i]))
			return false;
	return true;
}

int main() {
	int n;
	string str;
	scanf("%d\n", &n);
	for(int i = 0; i < n; i ++) {
		getline(cin, str);
		if(str.length() < 6) 
			printf("Your password is tai duan le.\n");
		else if(judge(str) == false) 
			printf("Your password is tai luan le.\n");
		else if(needNumber(str))
			printf("Your password needs shu zi.\n");
		else if(needAlpha(str))
			printf("Your password needs zi mu.\n");
		else 
			printf("Your password is wan mei.\n");
	}
	return 0;
}
  • B1082 射击比赛

#include<cstdio>

int main() {
	int n;
	scanf("%d", &n);
	int minDis = 30000, minId, maxDis = -1, maxId, id, x, y; 
	for(int i = 0; i < n; i ++) {
		scanf("%d %d %d", &id, &x, &y);
		if(x * x + y * y > maxDis) {
			maxDis = x * x + y * y;
			maxId = id;
		}
		if(x * x + y * y < minDis) {
			minDis = x * x + y * y;
			minId = id;
		} 
	}
	printf("%04d %04d", minId, maxId);
	return 0;
}
  • B1083 是否存在相等的差

用哈希表统计,大于1才输出。

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

const int maxn = 10000;
int hashTable[maxn] = {0};

int main() {
	int n, num;
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) {
		scanf("%d", &num);
		hashTable[abs(num - i)] ++;
	} 
	for(int i = maxn; i >= 0; i --) {
		if(hashTable[i] > 1)
			printf("%d %d\n", i, hashTable[i]);
	}
	return 0;
}
  • B1084 外观数列

核心是getNext函数,用于统计连续字符数,模拟了四十多分钟。。。

getNext():

string getNext(string s) {
	string next;
	int cnt = 0;
	char last = s[0];
	for(int i = 0; i < s.length(); i ++) {
		if(s[i] != last) {
			next += last;
			next += to_string(cnt);
			last = s[i];
			cnt = 1;
		}
		else {
			cnt ++;
		}
	}
	next += last;
	next += to_string(cnt);
	return next;
	
}

getNext一种更简单的写法(值得记住):

string getNext(string s) {
	string next;
	int j;
	for(int i = 0; i < s.length(); i = j) {
		for(j = i; j < s.length() && s[j] == s[i]; j++); 
//注意这里的分号,这行代码等价于后面注释的2行
//		j = i;
//		while(j < s.length() && s[j] == s[i]) j ++; 
		next += s[i] + to_string(j - i);
	}
	return next;
}

AC代码:


#include<cstdio>
#include<string>
using namespace std;

string getNext(string s) {
	string next;
	int cnt = 0;
	char last = s[0];
	for(int i = 0; i < s.length(); i ++) {
		if(s[i] != last) {
			next += last;
			next += to_string(cnt);
			last = s[i];
			cnt = 1;
		}
		else {
			cnt ++;
		}
	}
	next += last;
	next += to_string(cnt);
	return next;
	
}

string getNext2(string s) {
	string next;
	int j;
	for(int i = 0; i < s.length(); i = j) {
		for(j = i; j < s.length() && s[j] == s[i]; j++);
//		j = i;
//		while(j < s.length() && s[j] == s[i])
//			j ++; 
		next += s[i] + to_string(j - i);
	}
	return next;
}



int main() {
	int d, n;
	scanf("%d %d", &d, &n);
	string s = to_string(d); 
	string next = s;
	for(int i = 1; i < n; i ++) {
		next = getNext2(next);
	}
	printf("%s", next.c_str());
	return 0;
} 

  • B1085 PAT单位排行

debug de了半天,结果发现犯了几个低级错误,变量名写错了。。

坑点1:加权总分不能每次取整,先累加起来,最后加起来取整数部分。

坑点2:获得rank的时候需要用一个变量r或i来遍历,一开始写错写错前一个的rank + 1,测试结果不对都没发现。

总结:几处小的bug长时间没找到,就会怀疑逻辑上是否错了,浪费很多时间结果是低级错误,因此感觉逻辑上时正确的时候需要先检查程序代码是否意思正确。

#include<cstdio> 
#include<cstring>
#include<cctype>
#include<map>
#include<algorithm>
using namespace std;

const int N = 100010;
struct School {
	char id[7];
	int BScore, AScore, TScore;
	int totalScore;
	int stuNum;
	int rank;
} school[N];

void to_small(char id[]) {
	int len = strlen(id);
	for(int i = 0; i < len; i ++)
		id[i] = tolower(id[i]);
}

bool cmp(School a, School b) {
	if(a.totalScore != b.totalScore)
		return a.totalScore > b.totalScore;
	else if(a.stuNum != b.stuNum)
		return a.stuNum < b.stuNum;
	else
		return strcmp(a.id, b.id) < 0;
}

map<string, int> idx;

int main() {
	for(int i = 0; i < N; i ++) {
		school[i].AScore = school[i].BScore = school[i].TScore = 0;
		school[i].totalScore = 0;
		school[i].stuNum = school[i].rank = 0;
	}
	
	int n;
	scanf("%d", &n);
	char stuId[7], id[7];
	int score;
	int cnt = 1;
	for(int i = 1; i <= n; i ++) {
		scanf("%s %d %s", stuId, &score, id);
		to_small(id);
		if(idx[id] == 0) idx[id] = cnt ++;
		strcpy(school[idx[id]].id, id);
		if(stuId[0] == 'B') school[idx[id]].BScore += score;
		else if(stuId[0] == 'A') school[idx[id]].AScore += score;
		else if(stuId[0] == 'T')school[idx[id]].TScore += score;
		school[idx[id]].stuNum ++;
	}
	for(int i = 1; i <= cnt - 1; i ++) {
		school[i].totalScore = (int)(school[i].BScore / 1.5 + school[i].AScore + school[i].TScore * 1.5);
	}
	sort(school + 1, school + cnt, cmp);
	school[1].rank = 1;
	for(int i = 2; i <= cnt - 1; i ++) {
		if(school[i].totalScore == school[i - 1].totalScore)
			school[i].rank = school[i - 1].rank;
		else if(school[i].totalScore < school[i - 1].totalScore)
			school[i].rank = i;
	}
	printf("%d\n", cnt - 1);
	for(int i = 1; i <= cnt - 1; i ++) {
		printf("%d %s %d %d\n", school[i].rank, school[i].id, school[i].totalScore, school[i].stuNum);
	}
	return 0;
}
  • B1086 就不告诉你

坑点1:题目说不超过1000的整数看成不超过1000位了,还用了大整数,15分题不值得。。

坑点2:数字倒过来后前导0不能输出。

#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;
 
int main() {
	int a, b;
	scanf("%d %d", &a, &b);
	int c = a * b;
	string ans = to_string(c);
	reverse(ans.begin(), ans.end());
	while(ans[0] == '0')
		ans.erase(0, 1);
	printf("%s", ans.c_str()); 
	return 0;
}
  • B1088 三人行

第一反应是尝试自己解出个公式出来,但是发现解不出,没思路。后来意识到这种题应该利用计算机的思想,遍历求解,类似于遍历前2个条件,判断是否第3个条件的方法。但是按这种方法遍历最后有一个样例过不了。

坑点:看了柳神代码,才发现丙没说是整数,可以是double类型。。。

大体思路:用i表示甲,从99遍历到10,j表示乙是i的倒过来的数(j = i % 10 * 10 + i / 10; 一行代码搞定,我还傻傻写了个函数), k表示丙,double类型,是(j - i)* 1.0 / x; 判断j是否是k的y倍,第一个解就是答案。

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

int m, x, y;
void print(double t) {
	if(m == t) printf(" Ping");
	else if(m < t) printf(" Cong");
	else printf(" Gai");
}

int main() {
	scanf("%d %d %d", &m, &x, &y);
	for(int i = 99; i >= 10; i --) {
		int j = i % 10 * 10 + i / 10;
		double k = abs(j - i) * 1.0 / x;
		if(j == y * k) {
			printf("%d", i);
			print(i);print(j);print(k);
			return 0;
		}
	}
	printf("No Solution\n");
	return 0;
}
  • B1089 狼人杀-简单版

困难,看到题完全没思路。看了柳神的代码,确实好。

大体思路:设置一个说法数组v,和一个事实数组a。说话数组即输入,事实数组先初始化为1,表示好人,然后假设两个人i和j是坏人,设为-1。这样遍历所有的i ,j。

每次遍历设置i和j为坏人:a[i] = a[j] = -1; 然后遍历每个人的说法,如果和事实不符(即说法数组和事实数组的第k个元素异号)即为说谎,将说谎的人存入数组lie。

遍历完后检查是否符合要求:只有两个人说谎(lie.size() == 2),且一个狼人一个好人(一正一负,a[lie[0]] + a[lie[1]] == 0)。符合要求便输出退出。由于是从小到大遍历的,因此输出的就是最小序。

#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
int main() {
	int N;
	cin >> N;
	vector<int> v(N + 1);
	for(int i = 1; i <= N; i ++)
		cin >> v[i];
	for(int i = 1; i <= N; i ++) {
		for(int j = i + 1; j <= N; j ++) {
			vector<int> lie, a(N + 1, 1); //说谎,狼人-1/好人1 
			a[i] = a[j] = -1;
			for(int k = 1; k <= N; k ++)
				if(v[k] * a[abs(v[k])] < 0)  lie.push_back(k);
			if(lie.size() == 2 && a[lie[0]] + a[lie[1]] == 0) {
				cout << i << " " << j;
				return 0;
			}
		}
	}
	cout << "No Solution";
	return 0;
}
  • B1090 危险品装箱

用一个map存储不相容物品对。

注意到一个物品可能对应多个不相容物品,因此使用map<int, set<int>> mp来存储。

设置一个集合set<int> box用来存放一个集装箱中的物品编号,然后遍历这个set中的每一个物品,查看这个物品对应的所有不相容物品是否出现在本集合中,一旦出现,则flag为false,表示No。每轮结束需要情况box。

#include<cstdio>
#include<map>
#include<set>
using namespace std;

map<int, set<int>> mp;

int main() {
	int N, M, K;
	scanf("%d %d", &N, &M);
	for(int i = 0; i < N; i ++) {
		int id1, id2;
		scanf("%d %d", &id1, &id2);
		mp[id1].insert(id2);
		mp[id2].insert(id1);
	} 
	set<int> box;
	for(int i = 0; i < M; i ++) {
		scanf("%d", &K);
		int id;
		for(int j = 0; j < K; j ++) {
			scanf("%d", &id);
			box.insert(id);
		}
		bool flag = true;
		for(auto it = box.begin(); it != box.end(); it ++) {
			for(auto it2 = mp[*it].begin(); it2 != mp[*it].end(); it2 ++) {
				if(box.find(*it2) != box.end()) {
					flag = false;
					break;
				}
			}
		}
		if(flag == false) printf("No\n");
		else printf("Yes\n");
		box.clear();
	}
	return 0;
}
  • B1091 N-自守数

在数字串中查找符合条件的数字串即可。

坑点:我一开始的判断逻辑是这样的:

int pos = num.find(it);
if(pos == num.length() - it.length()) {
	printf("%d %d\n", i, i * k * k);
	break;
}

但是最后一个样例过不了。在num找到it的位置如果等于num的长度减去it的长度,说明it应该是num的后缀了。后来想可能长度不一样,于是加了一个num[len - 1] == it[len - 1],以为这样可以保证是后缀了。但还是不对,应该还是存在反例。 后来仔细一样既然要求后缀相同,那直接比较不就行了。。。于是用substr可以过了。

#include<cstdio>
#include<string>
using namespace std;

void judge(int k) {
	string it = to_string(k);
	int i;
	for(i = 1; i < 10; i ++) {
		string num = to_string(i * k * k);
		if(num.substr(num.size() - it.size()) == it) {
			printf("%d %d\n", i, i * k * k);
			break;
		}
	}
	if(i == 10) printf("No\n");
}


int main() {
	int M, K;
	scanf("%d", &M);
	for(int i = 0; i < M; i ++) {
		scanf("%d", &K);
		judge(K);
	}
	return 0;
}
  • B1092 最好吃的月饼

按列求和,求和最大的列的列号。相同的最大值存储起来。

#include<cstdio>

int a[110][1010];
int champ[1010];

int main() {
	int N, M;
	scanf("%d %d", &N, &M);
	for(int i = 0; i < M; i ++) {
		for(int j = 0; j < N; j ++) {
			scanf("%d", &a[i][j]);
		}
	} 
	int maxAmount = 0, num = 0;
	for(int i = 0; i < N; i ++) {
		int sum = 0;
		for(int row = 0; row < M; row ++)
			sum += a[row][i];
		if(sum > maxAmount) {
			maxAmount = sum;
			num = 0;
			champ[num ++] = i + 1;
		}
		else if(sum == maxAmount) {
			champ[num ++] = i + 1;
		}
	}
	printf("%d\n", maxAmount);
	for(int i = 0; i < num; i ++) {
		if(i > 0) printf(" ");
		printf("%d", champ[i]);
	}
	return 0;
}
  • B1093 字符串A+B

哈希的思想,将出现的字符hash表中的+1,只有第一次出现才能输出。

scanf()读一行的方法:scanf("%[^\n]*c"); 测试发现不会吞末尾的换行。需要getchar()。

#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 2000020;
int hashTable[200] = {0};
char str1[maxn / 2], str2[maxn / 2];

int main() {
	scanf("%[^\n]*c", str1);
	getchar();
	scanf("%[^\n]*c", str2);
	for(int i = 0; i < strlen(str1); i ++) {
		if(hashTable[str1[i]] == 0) {
			hashTable[str1[i]] ++;
			printf("%c", str1[i]);
		}
	} 
	for(int i = 0; i < strlen(str2); i ++) {
		if(hashTable[str2[i]] == 0) {
			hashTable[str2[i]] ++;
			printf("%c", str2[i]);
		}
	} 
}
  • B1094 谷歌的招聘

我的思路:得到素数表,遍历素数表,将素数转换为字符串,不够K位补前导0,然后在字符串中查找索引最小的素数。结果最后一个样例过不了。。

柳神的思路:遍历数串的长度为K的子串判断是否为素数。够简洁。。。

注意遍历条件是:i + K <= L

#include<cstdio>
#include<iostream> 
#include<string>
using namespace std;

bool isPrime(int n) {
	if(n == 0 || n == 1) return false;
	for(int i = 2; i * i <= n; i ++)
		if(n % i == 0) return false;
	return true;
}

int main() {
	int L, K;
	scanf("%d %d", &L, &K);
	string N;
	cin >> N;
	bool flag = false;
	for(int i = 0; i + K <= L; i ++) {
		if(isPrime(stoi(N.substr(i, K)))) {
			printf("%s", N.substr(i, K).c_str());
			flag = true;
			break;
		}
	}
	if(flag == false) printf("404");
	return 0;
} 
  • B1095 解码PAT准考证

我的坑点:排序规则cmp中忘记id升序了。

我的思路:设置一个学生数组,输入id和分数,输入后按分数和id排好序。对于类型1的查询,遍历数组符合条件就输出即可;对于类型2的查询,遍历统计即可。对于类型3的查询,先用map将编号和数量记录下来,然后按值排序,由于map不能直接排序,因此设一个vector<pair<string, int>>数组将map中元素输入,然后对这个vector按cmpMap规则排序,最后输出即可。

由于map有测试点超时,改用unordered_map即可。(unordered_map基于哈希表更快)

#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#include<unordered_map>
#include<vector> 
using namespace std;

const int maxn = 10010;
struct Student {
	string id;
	int score;
} stu[maxn];

bool cmp(Student a, Student b) {
	if(a.score != b.score)
		return a.score > b.score;
	else
		return a.id < b.id;
}

bool cmpMap(const pair<string, int>& a, const pair<string, int>& b) {
	if(a.second != b.second)
		return a.second > b.second;
	else 
		return a.first < b.first;
}

int main() {
	int N, M;
	scanf("%d %d", &N, &M);
	for(int i = 0; i < N; i ++) {
		cin >> stu[i].id >> stu[i].score;
	}
	sort(stu, stu + N, cmp);
	int type;
	string cmd;
	for(int k = 0; k < M; k ++) {
		cin >> type >> cmd;
		printf("Case %d: %d %s\n", k + 1, type, cmd.c_str());
        int cnt = 0, sum = 0;
		if(type == 1) {
			bool flag = false;
			for(int i = 0; i < N; i ++) {
				if(stu[i].id[0] == cmd[0]) {
					flag = true;
					printf("%s %d\n", stu[i].id.c_str(), stu[i].score);
				}
			} 
			if(flag == false) printf("NA\n");
		}
		else if(type == 2) {
			
			for(int i = 0; i < N; i ++) {
				if(stu[i].id.substr(1, 3) == cmd) {
					cnt ++;
					sum += stu[i].score;
				}
			}
			if(cnt != 0) printf("%d %d\n", cnt, sum);
			else printf("NA\n");
		}
		else if(type == 3) {
			unordered_map<string, int> mp;
			for(int i = 0; i < N; i ++) {
				if(stu[i].id.substr(4, 6) == cmd) {
					mp[stu[i].id.substr(1, 3)] ++;
				}
			}
			vector<pair<string, int>> v(mp.begin(), mp.end());
			sort(v.begin(), v.end(), cmpMap);
			for(int i = 0; i < v.size(); i ++)
				printf("%s %d\n", v[i].first.c_str(), v[i].second);
			if(v.size() == 0) printf("NA\n");
		}
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值