《算法笔记》学习日记——6.1 vector的常见用法详解

6.1 vector的常见用法详解

Codeup Contest ID:100000596

问题 A: Course List for Student (25)

题目描述
Zhejiang University has 40000 students and provides 2500 courses. Now given the student name lists of all the courses, you are supposed to output the registered course list for each student who comes for a query.
输入
Each input file contains one test case. For each case, the first line contains 2 positive integers: N (<=40000), the number of students who look for their course lists, and K (<=2500), the total number of courses. Then the student name lists are given for the courses (numbered from 1 to K) in the following format: for each course i, first the course index i and the number of registered students Ni (<= 200) are given in a line. Then in the next line, Ni student names are given. A student name consists of 3 capital English letters plus a one-digit number. Finally the last line contains the N names of students who come for a query. All the names and numbers in a line are separated by a space.
输出
For each test case, print your results in N lines. Each line corresponds to one student, in the following format: first print the student’s name, then the total number of registered courses of that student, and finally the indices of the courses in increasing order. The query results must be printed in the same order as input. All the data in a line must be separated by a space, with no extra space at the end of the line.
样例输入

11 5
4 7
BOB5 DON2 FRA8 JAY9 KAT3 LOR6 ZOE1
1 4
ANN0 BOB5 JAY9 LOR6
2 7
ANN0 BOB5 FRA8 JAY9 JOE4 KAT3 LOR6
3 1
BOB5
5 9
AMY7 ANN0 BOB5 DON2 FRA8 JAY9 KAT3 LOR6 ZOE1
ZOE1 ANN0 BOB5 JOE4 JAY9 FRA8 DON2 AMY7 KAT3 LOR6 NON9

样例输出

ZOE1 2 4 5
ANN0 3 1 2 5
BOB5 5 1 2 3 4 5
JOE4 1 2
JAY9 4 1 2 4 5
FRA8 3 2 4 5
DON2 2 4 5
AMY7 1 5
KAT3 3 2 4 5
LOR6 4 1 2 4 5
NON9 0

思路
这题的大致意思是:N是要查询的学生人数,K是课的数目,然后有K行,每行给出一个课的编号i,和要上这门课的学生人数Ni,然后下一行是要上这门课的学生名字。

最直观的思路就是像题目一样把输入的每行学生名字都存在一个二维数组里,行号代表课的编号,每一列就是一堆学生名字(用结构体)。最后把名单存储在一个字符串数组里,然后再通过for循环查找名单里每一个学生(O(N)),再查找每行(O(K))的每一个学生(O(Ni)),判断其名字是否与待查找的学生名字相等,如果相等,则方案数cnt++,并且把当前的行号压入到暂存的temp向量中,查找完毕之后输出即可。但是,这么做时间肯定是会超限的,三重循环的查找,时间复杂度是O(N×K×Ni)=O(40000×2500×200),显然是过大的。

在这里我建议大家,有想法就去做,不要怕难,其实我一开始根据输出结果就想到用二维数组的散列来存储了,行是学生姓名,列是int型的一维散列表,但是因为害怕字符串对整数类型的hash映射(26进制或者更多的进制数转10进制的方法),所以没有做下去,换了一种O(N3)的直观方法来做,但是那样做的话也就25分里拿23分了(PAT我提交过,最后一个测试点超时),如果实在想不起来字符串的hash函数怎么写的话,就这样做好了,但是想要拿满分的话还是得牢记字符串的hash呀。

于是优化后的思路就是:开一个大小为26×26×26×10+10(3个大写字母和1个数字,因此是263×10+10)的int型vector(其实就是26×26×26×10+10个int型vector容器),行号对应学生姓名映射过来的整数型id,每一个vector里面存储的是对应那个学生要上的课的编号。然后每次输入姓名的时候就直接映射成id,把这门课的编号push_back到对应的容器里。最后根据id依次输出每个容器里的内容就行了,需要注意的是,因为压入的时候可能是大的数字在前(比如题目给的样例,先是4,再是1、2、3、5),所以vector容器里可能是乱序,因此每次准备输出这个容器里的所有元素之前,需要用sort函数给容器里的元素排序。

当然,这题你也可以用二维数组来做,行号是id,列号是所有课程的编号,因此第二维最多开到2501,每次读入一个课的编号index,就把stu[id][index]置1,然后最后遍历这一行的所有元素,输出值是1的下标即可(这么一想是不是vector要快速多了,可以免去很多无效的查询)。
代码

///直观的超时方案///
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<algorithm>
#include<vector>
using namespace std;
struct student{
	char name[5];
	student(){
		memset(name, 0, sizeof(name));
	}
};
vector<student> stu[2501];
vector<int> temp;
char name[40000][5];
int main(){
	int N, K;
	while(scanf("%d%d", &N, &K) != EOF){
		for(int i=1;i<=K;i++){
			int index, n;
			scanf("%d%d", &index, &n);
			for(int j=1;j<=n;j++){
				student tmp;
				scanf("%s", tmp.name);
				stu[index].push_back(tmp);
			}
		}
		for(int i=0;i<N;i++) scanf("%s", name[i]);
		for(int i=0;i<N;i++){
			int cnt = 0;
			for(int j=1;j<=K;j++){
				for(int k=0;k<stu[j].size();k++){
					if(strcmp(name[i], stu[j][k].name)==0){
						cnt++;
						temp.push_back(j);
					}
				}
			}
			printf("%s %d", name[i], cnt);
			for(int j=0;j<temp.size();j++) printf(" %d", temp[j]);
			printf("\n");
			temp.clear();
		}
	}
	return 0;
}
///优化后的代码///
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 26*26*26*10+10;//3个大写字母加1个数字
int getid(char s[], int len){//建立字符串到整数的hash 
	int id = 0;
	for(int i=0;i<len-1;i++){//末尾是数字,除掉末尾
		id = id*26 + (s[i]-'A'); 
	}
	id = id*10+(s[len-1]-'0');//把最后一位数字直接拼接上去 
	return id;
}
vector<int> stu[maxn];//行号是学生姓名对应的id,每一个vector容器里又包含着学生所选的课 
char name[40000][5];
int main(){
	int N, K;
	while(scanf("%d%d", &N, &K) != EOF){
		for(int i=1;i<=K;i++){
			int index, n;//index是课的编号,n是选这门课的学生人数 
			scanf("%d%d", &index, &n);
			for(int j=1;j<=n;j++){
				char temp[5];
				int len = 0;
				scanf("%s", temp);
				int id = getid(temp, strlen(temp));
				stu[id].push_back(index);//给对应学生压入课的编号
			}
		}
		for(int i=0;i<N;i++) scanf("%s", name[i]);//输入待查询的学生姓名
		for(int i=0;i<N;i++){
			int id = getid(name[i], strlen(name[i]));
			sort(stu[id].begin(), stu[id].end());
			printf("%s %d", name[i], stu[id].size());
			for(int j=0;j<stu[id].size();j++){
				printf(" %d", stu[id][j]);
			}
			printf("\n");
		} 
	}
	return 0;
}

问题 B: Student List for Course (25)

题目描述
Zhejiang University has 40000 students and provides 2500 courses. Now given the registered course list of each student, you are supposed to output the student name lists of all the courses.
输入
Each input file contains one test case. For each case, the first line contains 2 numbers: N (<=40000), the total number of students, and K (<=2500), the total number of courses. Then N lines follow, each contains a student’s name (3 capital English letters plus a one-digit number), a positive number C (<=20) which is the number of courses that this student has registered, and then followed by C course numbers. For the sake of simplicity, the courses are numbered from 1 to K.
输出
For each test case, print the student name lists of all the courses in increasing order of the course numbers. For each course, first print in one line the course number and the number of registered students, separated by a space. Then output the students’ names in alphabetical order. Each name occupies a line.
样例输入

10 5
ZOE1 2 4 5
ANN0 3 5 2 1
BOB5 5 3 4 2 1 5
JOE4 1 2
JAY9 4 1 2 5 4
FRA8 3 4 2 5
DON2 2 4 5
AMY7 1 5
KAT3 3 5 4 2
LOR6 4 2 4 1 5

样例输出

1 4
ANN0
BOB5
JAY9
LOR6
2 7
ANN0
BOB5
FRA8
JAY9
JOE4
KAT3
LOR6
3 1
BOB5
4 7
BOB5
DON2
FRA8
JAY9
KAT3
LOR6
ZOE1
5 9
AMY7
ANN0
BOB5
DON2
FRA8
JAY9
KAT3
LOR6
ZOE1

思路
这一题和上一题正好倒过来,上一题是让你输出每个学生要上哪些课,这一题是让你输出每门课有哪几个学生要上。
为了方便,我这里直接用了C++里的<string>(注意,和<string.h>不是同一个东西,另外,想要输入输出整个字符串,只能用C++里的cin和cout(在<iostream>里面)),声明了里面存放字符串类型的vector,这样的话,每个vector里就能存放一个又一个学生的名字了。同样的道理,下标对应的是课程的编号,于是声明的最大值应该是2500,每次输入一个课程编号的时候,就把学生的姓名压入对应的course[tmp]中。

然而很不幸的是……以上思路的代码虽然在codeupAC了,但是PAT还是超时了(难道是cin和cout的问题??)
于是我们需要沿用上一题的思路,用数字来代替每个人的名字……
代码

///codeupAC,PAT超时///
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<algorithm>
#include<vector>
#include<string>
#include<iostream>
using namespace std;
vector<string> course[2501];
int main(){
	int N, K;
	while(cin>>N>>K){
		for(int i=1;i<=N;i++){
			string tempName;
			int C;
			cin>>tempName>>C;
			for(int j=1;j<=C;j++){
				int tmp;
				cin>>tmp;
				course[tmp].push_back(tempName);
			}
		}
		for(int i=1;i<=K;i++){
			cout<<i<<' '<<course[i].size()<<endl;
			sort(course[i].begin(), course[i].end());
			for(int j=0;j<course[i].size();j++) cout<<course[i][j]<<endl;
		}
	} 
	return 0;
}
///优化后的代码///
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<algorithm>
#include<vector>
#include<string>
#include<iostream>
using namespace std;
vector<int> course[2501];
char name[40001][5];
bool cmp(int a, int b){
	return strcmp(name[a], name[b])<0;//字典序升序 
}
int main(){
	int N, K;
	while(scanf("%d%d", &N, &K) != EOF){
		for(int i=1;i<=N;i++){
			scanf("%s", name[i]);//i号学生 
			int C;
			scanf("%d", &C);
			for(int j=1;j<=C;j++){
				int tmp;
				scanf("%d", &tmp);
				course[tmp].push_back(i);//把i号学生的i压入 
			}
		}
		for(int i=1;i<=K;i++){
			printf("%d %d\n", i, course[i].size());
			sort(course[i].begin(), course[i].end(), cmp);
			for(int j=0;j<course[i].size();j++) printf("%s\n", name[course[i][j]]);
			//course[i][j]是存储的几号学生,只要对应回name字符串数组即可输出对应的学生名字 
		} 
	} 
	return 0;
}

小结

初见vector可能比较受苦?(做题做出了玩黑魂的感觉hhh),虽然我一开始做问题A的时候极其抵触vector,因为用不来也理解不了,想要用二维数组去做。但是用熟练了之后可以看到vector其实是一种特别方便的数组(或者说线性表?),尤其是对于这些需要hash的题目来说,相比于普通的二维数组,查找快了太多太多(上面也说到过的,如果是普通二维数组,要输出值为1或是true的下标,则会存在大量的查找0或false的操作,时间白白浪费)。
在这里建议想要用二维的vector还是定义为:

vector<typename> Arrayname[arraySize];

这样比较好,因为如果定义为:

vector<vector<typename> >

这样子的话,其实是不便于题目上的操作的。
总的来说,通过这两题的练习,一来熟练了vector的用法,二来巩固了hash和sort的知识(sort也是可以用于vector排序的哈,非常方便~),可以说是收获颇丰了^^。

发布了54 篇原创文章 · 获赞 27 · 访问量 4990

猜你喜欢

转载自blog.csdn.net/weixin_42257812/article/details/105222410
今日推荐