题目大意就是:
-
输入N和K(N是图中边的数量, K是阈值)
-
然后输入N条边:弧头 弧尾 弧权值
-
如果一个连通块满足:
- 所有边权值的总和必须要大于K
- 人数(顶点数)大于2(3个及以上)
那么该连通块就是一个Gang(而Gang的头头就是最大权值的顶点)
输出这个图中所有Gang的头头的名字(最大权值顶点的名字)和这个Gang的成员个数(单个连通块中顶点个数)
题中点权值等于和它相连的所有边的权值总和
想了一晚上,终于想出来适合自己的DFS邻接表使用的数据结构。
- 当难以将顶点信息
类型为VexType
做为索引的时候,就需要用map<VexType, vector<ArcNode>> G;
代替vector<VexType> G[顶点个数]
,做成顶点信息VexType
和有向边集合vector<ArcNode>
的映射 - 因为是map,所以没有出度的边不会出现在上面的G中,如果要访问顶点的权值的话,还需要一个额外的顶点表
map<VexType, int> V
- 因为不会对
ArcNode边
进行复杂的操作,而只是访问操作,所以可以在ArcNode
中加入一个访问标记vis
(至于为什么不把顶点设置成一个结构体然后也设置一个访问标记vis,我觉得是因为要对顶点做的操作太多了,设置成结构体增加复杂性) - 当顶点信息是字符串时,可以对字符串进行hash再使用vector,就可以使用原来形式的图数据结构了(这个以后再试试)
# include <iostream>
# include <vector>
# include <map>
using namespace std;
/*
如果一个连通块:
所有边权值的sum必须要大于K
人数为大于2(3个及以上)
满足以上两个条件,那么就是一个Gang(而Gang的头头就是最大权值的顶点)
然后输出Gang的总个数 and
所有头头的名字和当前头头的Gang的总人数
*/
typedef string VexType;
// 边结点
struct ArcNode{
VexType name; // 顶点名字(边的弧头)
int weight; // 以顶点为弧尾的边的权重
bool vis; // 标记当前边是否被访问过
ArcNode(VexType _n, int _w, bool _v): name(_n), weight(_w), vis(_v){
}
};
// 当难以将顶点信息做为索引的时候,就需要用map做成代替vector(顶点信息是字符串时,可以对字符串进行hash再使用vector)
// V[u]得到的是顶点u的权值
map<VexType, int> V; // 顶点表(顶点名字:顶点权重)
// 当要访问没有没有出度的顶点时,因为map对这些没加入map的使用默认初始化,所以没有出度的顶点的vector为空,也就不影响了
// G[u]得到的是u的邻接点的vector(或者说有向边集合)
map<VexType, vector<ArcNode>> G; // 邻接表(顶点名字:可达的邻接点集合(有的顶点没有出度,也就不会在这个映射里了))
map<VexType, bool> visited; // 标记顶点是否访问
map<VexType, int> Gang; // 某Gang的头头:帮派权重总和
int N, K;
// 因为一次调用DFS都会遍历一整个连通块,所以下面三个变量是针对一个连通块的
// maxWeightVex:最大权值点 sumArcWeight:边权之和 numPeople当前连通块的顶点个数
void DFS(VexType u, VexType &maxWeightVex, int &sumArcWeight, int &numPeople){
// 访问u(这里对点u进行处理访问)
numPeople++;
if(V[u] > V[maxWeightVex]) // 如果当前顶点的权值大于之前找到的最大权值
maxWeightVex = u;
visited[u] = true;
// 遍历u的所有邻接点(这里对从点u出去的边进行处理访问)
for(ArcNode &arcnode: G[u]){
// 似乎不用加引用也可以
VexType v = arcnode.name; // v是邻接点的名字
if(arcnode.vis == false){
// 如果当前边的边权还没加入sumArcWeight中(也就是没被访问过),那就加入并标记已访问
sumArcWeight += arcnode.weight;
arcnode.vis = true;
}
// 如果u的当前邻接点未被访问就去访问,若已访问就进入下一次循环判断下一个邻接点
if(visited[v] == false){
DFS(v, maxWeightVex, sumArcWeight, numPeople); // 去到邻接点v访问邻接点v
}
}
}
void DFSTraverse(){
/* 遍历不同的连通块
(通过寻找未访问过的节点,然后DFS访问它,然后它所在的连通块中的所有结点就会被访问,
于是下一次寻找的未访问的顶点一定是别的连通块)
*/
for(auto p: V){
// 遍历顶点表
VexType u = p.first; // u是顶点名字
if(visited[u] == false){
// maxWVex:最大权值点 sumW:边权之和 numPeople当前连通块的顶点个数
VexType maxWVex = u;
int sumW = 0;
int numPeople = 0;
DFS(u ,maxWVex, sumW, numPeople);
// 如果连通块总人数3人以上 && 总边权大于K,让就加入Gang集合
if(numPeople >= 3 && sumW > K)
Gang[maxWVex] = numPeople;
}
}
}
int main(){
cin >> N >> K;
for(int i=0;i<N;++i){
VexType name1, name2;
int weight;
cin >> name1 >> name2 >> weight;
G[name1].push_back({
name2, weight, false}); // 建立邻接表,设置边权和初始化为未访问过
V[name1] += weight; // 设置点权
V[name2] += weight; // 设置点权
}
DFSTraverse();
// 然后输出Gang的总个数
cout << Gang.size() << endl;
for(auto p: Gang){
// 输出头头的名字和当前头头的Gang的总人数
cout << p.first << " " << p.second << endl;
}
return 0;
}
下面附上模板DFS邻接表的模板:
# include <iostream>
# include <vector>
# include <map>
using namespace std;
typedef string VexType;
// 边结点
struct ArcNode{
VexType name; // 顶点名字(边的弧头)
int weight; // 以顶点为弧尾的边的权重
bool vis; // 标记当前边是否被访问过
ArcNode(VexType _n, int _w, bool _v): name(_n), weight(_w), vis(_v){
}
};
// 当难以将顶点信息做为索引的时候,就需要用map做成代替vector(顶点信息是字符串时,可以对字符串进行hash再使用vector)
map<VexType, int> V; // 顶点表(顶点名字:顶点权重)
// 当要访问没有没有出度的顶点时,因为map对这些没加入map的使用默认初始化,所以没有出度的顶点的vector为空,也就不影响了
// G[u]得到的是u的邻接点的vector
map<VexType, vector<ArcNode>> G; // 邻接表(顶点名字:可达的邻接点集合(有的顶点没有出度,也就不会在这个映射里了))
map<VexType, bool> visited; // 标记顶点是否访问
int N; // 边的个数
/* 一次调用DFS都会遍历一整个连通块
>>> 要统计当前连通块的数据的话可以在参数中添加变量(记得统计的变量是引用【如果不是全局变量的话】),
>>> 如果是对点做统计那就在访问点那里写语句,
>>> 如果是对边做统计的话就在访问边那里写)
*/
void DFS(VexType u){
// 访问u(这里对点u进行处理访问)
cout << "点" <<u << endl;
visited[u] = true;
// 遍历u的所有邻接点(这里对从点u出去的边进行处理访问)
for(ArcNode &arcnode: G[u]){
// 似乎不用加引用也可以
VexType v = arcnode.name; // v是u邻接点的名字
if(arcnode.vis == false){
// 如果当前边没被访问过,那就访问并标记
// 访问边
cout << "边" <<'[' << u << "-" << v << ']' << endl;
arcnode.vis = true;
}
// 如果u的当前邻接点未被访问就去访问,若已访问就进入下一次循环判断下一个邻接点
if(visited[v] == false){
DFS(v); // 去到邻接点v访问邻接点v
}
}
}
void DFSTraverse(){
/* 遍历不同的连通块
(通过寻找未访问过的节点,然后DFS访问它,然后它所在的连通块中的所有结点就会被访问,
于是下一次寻找的未访问的顶点一定是别的连通块)
*/
int cnt = 0;
for(auto p: V){
// 遍历顶点表
VexType u = p.first; // u是顶点名字
// 有多少个连通块这个if里面的语句就会被执行多少次
if(visited[u] == false){
// 可以在这里设立对一个连通块统计的变量
printf("\n连通块%d:\n", (cnt++)+1);
DFS(u);
// 可以在这里处理在DFS里统计的数据
cout << endl;
}
}
}
int main(){
cin >> N >> K;
for(int i=0;i<N;++i){
VexType name1, name2;
int weight;
cin >> name1 >> name2 >> weight;
G[name1].push_back({
name2, weight, false}); // 建立邻接表,设置边权和初始化为未访问过
V[name1] += weight; // 设置点权
V[name2] += weight; // 设置点权
}
DFSTraverse();
return 0;
}