蓝桥杯算法提高VIP-卡勒沃夫之弱水路三千(提高型)

题目

题目链接

题解

拓扑排序(应该也有其他方法,但对于这类题拓扑是通解)。
本题解对会拓扑和STL的同学比较友好,不会的同学可以去别的博客学习一下,毕竟总会用到的。


在蓝桥杯里,也做过好几个拓扑的题了。
如果你会拓扑,那么这个题难在如何对使用合适的STL保存数据和处理数据。

详细介绍一下代码中变量的含义后,配合代码注释食用最佳。
就是一道拓扑模板题,但是所用数据结构比较麻烦难以理解。


map<string, int> idid[str] = c 表示字符串str的编号为c,在本题中一个名字对应一个编号,此数据结构用于记录每一个名字的编号是多少。之所以采用编号的思想是因为后面在进行构造拓扑的无向图时会比较方便,而用字符串难以实现;
ids:每遇到一个没有出现过的字符串,就要为这个字符串设置编号ids,出现一个新字符串ids++。用于给字符串编号;
string s1, s2:输入的字符串;
string ans[N]:输出拓扑序列就是通过这个函数输出的,即保存答案,ans[i]排序为i的字符串是什么;
string nameofid[N]:编号对应的名字是什么,nameofid[i]表示编号为i的名字是什么;
vector<string> name:全部名字都保存在这里,不重复存;
int ne[M], e[M], h[N], idx:这几个应该比较熟悉,就是邻接表的几个变量。ne[i]e[i]都表示边的编号为i对应的点编号,h[i]表示名字对应编号为i对应的边编号;
T, n:如题;
in[N]in[i]表示名字编号为i对应的入度,拓扑排序中用到;
cnt:控制ans数组的个数。这个题目的意思就是必然存在拓扑序列,因此最终ans的元素数量其实就是出现的字符串的数量,即ids = cnt

代码

// https://www.dotcpp.com/oj/problem1506.html
#include<bits/stdc++.h>
using namespace std;
const int N = 110; // 题目说最多13个人,不知道为什么只有开到100+才能不出“运行错误” 
const int M = 110;


map<string, int> id;
int ne[M], e[M], h[N], T, n, in[N], cnt, ids, idx;
string s1, s2, ans[N], nameofid[N];
vector<string> name;

void add(int a, int b) { // 传入的参数为名字对应的编号 
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void topsort() {
	
	int m = name.size();
	queue<string> q; // 元素为名字 
	
	for(int i = 0;i < m;i ++) if(in[id[name[i]]] == 0) q.push(name[i]), ans[++cnt] = name[i]; // 将一开始入度为0的名字加入队列和答案数组中 
	
	while(!q.empty()) {
		string t = q.front();
		q.pop();
		for(int i = h[id[t]];i != -1;i = ne[i]) { // id[t]为取出的队头名字对应的id,h[id]表示id这个点对应的边 
			int j = e[i]; // 这是名字的编号 
			string ss = nameofid[j]; // 编号对应的名字 
			in[j]--; // 入度-- 
			if(in[j] == 0) q.push(ss), ans[++cnt] = ss; // 入度为0加入队列和答案 
		}
	}
}

int main()
{
	cin>>T;
	while(T--) {
		cin>>n;
		
		// 初始化 
		id.clear();name.clear();
		memset(in, 0, sizeof in);
		memset(h, -1, sizeof h);
		cnt = 0, idx = 0, ids = 0;
		
		while(n--) {
			cin>>s1>>s2;
			if(!id[s1]) id[s1] = ++ ids, nameofid[ids] = s1, name.push_back(s1); // 新出现的就ids++ 
			if(!id[s2]) id[s2] = ++ ids, nameofid[ids] = s2, name.push_back(s2);
			add(id[s1], id[s2]);
			in[id[s2]] ++;
		}	
		
		topsort();
		
		for(int i = 1;i <= cnt;i ++) cout << ans[i] << ' '; cout<<endl;
	} 

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不牌不改

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值