【HZNU Summer training】CF-129B Students and Shoelaces (拓扑排序)

    此题的题意是,一个社团里有一些学生,他们互相之间存在联系(即用鞋带互相绑着,好雅兴啊),暴躁的社团负责人要找出那些只和一个人绑着鞋带的学生,然后把他们踢掉,随后回到上一步继续找,问需要踢几轮,才可不能再踢。

    起初看到连接关系,还以为是并查集。读完题发现是要剔除无向图的一度顶点,那么自然而然想到拓扑排序,将第一层排出的节点剔除,然后不断重复这个操作,直到没有一度顶点。

    先看代码(最近中了模块化编程的毒,代码风格有点诡异,将就着看好了):

#include<iostream>
#include<cmath>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<vector>
#include<cstdio>
#define INF 0x3f3f3f3f
using namespace std;
int n,m,ans=0,cnt=0,degree_one[105];
map<int,int>mp;
queue<int>q;
vector<int>v[105];
void Input() {                   //输入
	cin>>n>>m;
	for(int i=0; i<m; i++) {
		int a,b;
		cin>>a>>b;
		v[a].push_back(b);
		v[b].push_back(a);
		mp[a]++;
		mp[b]++;
	}
}
void Get_Degree_One_Node() {      //找出一度定点,并压入队列
	for(int i=1; i<=n; i++) {
		if(mp[i]==1) q.push(i);
	}
}
void Count_Degree_One_And_Update_Answer() { //统计一度顶点的个数,如果可以踢一轮,就更新答案
	while(!q.empty()) {
		degree_one[cnt]=q.front();
		q.pop();
		cnt++;
	}
	if(cnt>0) ans++;
}
void Clear_Degree_One_Node() {   //将与先前踢出的顶点有关的顶点的度数更新,这样就能产生新的一度顶点
	for(int i=0; i<cnt; i++) {
		int temp=degree_one[i];
		mp[temp]--;
		for(int j=0; j<v[temp].size(); j++) {
			int neighbor=v[temp][j];
			if(mp[neighbor]>0) mp[neighbor]--;
		}
	}
}
int main() {
	Input();
	Get_Degree_One_Node();
	while(!q.empty()) {
		cnt=0;
		memset(degree_one,0,sizeof(degree_one));
		Count_Degree_One_And_Update_Answer();
		Clear_Degree_One_Node();
		Get_Degree_One_Node();
	}
	cout<<ans<<endl;
	return 0;
}

    此代码中,我用一个map来存每个顶点的度数,用二维vector来表示与这个顶点相连接的点(第一个下标表示某一顶点,里面的元素表示这一顶点的相邻顶点,相当于邻接表),用degree_one数组存得到的一度顶点。在输入时就先更新输入的两点的状态——互相加入对方的邻接顶点集合,并且更新度数。随后遍历map来找出一度顶点压入队列,并统计这个队列的元素个数,如果至少有一个元素,那么就需要新踢一轮,更新ans并把队列清空。

    更新完答案是不是可以继续找下一轮了?还得收拾残局,因为将一度顶点剔除后,剩下的顶点的度数会发生变化,有可能产生新的一度顶点,因此需要遍历剩下的顶点,查询它们的邻接表,如果存在刚被剔除的顶点(已经存在degree_one数组中),就更新它们的度数。重复这些操作,最后就能得出答案了。

猜你喜欢

转载自blog.csdn.net/linyiduo123/article/details/81203583
今日推荐