UVa247 Calling Circles (Floyd +DFS/并查集)

本题中两点A,B在一个一个calling circle中的条件是存在一条A->B的路径,也存在一条B->A的路径。可以用Floyd算法传递闭包,

g[i][j] 为1表示i->j存在路径,反之表示不存在,那么AB在一个circle中等价于 g[A][B]&&g[B][A]。

可以用DFS或者并查集来输出连通分量。

本题的顶点以string表示,可以用STL map建立一个姓名到数字的映射,再建立一个数字到名字的映射,便于建图和输出。
DFS版本(简单,推荐)(我也不知道我开始的时候怎么想的非要用并查集==,超麻烦的)。

#include<iostream>
#include<cstring>
#include<map>
#include<string>
#include<vector>
#include<algorithm>
#define maxn 30
using namespace std;
int n, m,g[maxn][maxn],vis[maxn];
map<string, int> nton;
map<int, string> rev;
vector<vector<int> > ans;
void dfs(int cur, int flag)
{
	if (flag) cout << ", ";
	cout << rev[cur];
	vis[cur] = 1;
	for (int j = 0; j < n; j++)
		if (!vis[j] && g[cur][j] && g[j][cur])
			dfs(j, 1);
}
int main()
{
	int kase = 0;
	while (cin>>n>>m && n)
	{
		memset(g, 0, sizeof(g));
		nton.clear();
		rev.clear();
		int ncnt = 0,t1,t2;
		string n1, n2;
		for (int i = 0; i < m; i++)
		{
			cin >> n1 >> n2;
			if (nton.count(n1)) t1 = nton[n1];
			else t1 = nton[n1] = ncnt++;
			if (nton.count(n2)) t2 = nton[n2];
			else t2 = nton[n2] = ncnt++;
			g[t1][t2] = 1;
		}
		for (int k = 0; k < n; k++)
			for (int i = 0; i < n; i++)
				for (int j = 0; j < n; j++)
					g[i][j] = g[i][j] || (g[i][k] && g[k][j]);
		for (auto it = nton.begin(); it != nton.end(); it++)
			rev[it->second] = it->first;
		memset(vis, 0, sizeof(vis));
		if (kase) cout << endl;
		cout << "Calling circles for data set " << ++kase << ':' << endl;
		for(int i=0;i<n;i++)
			if (!vis[i])
			{
				dfs(i, 0);
				cout << endl;
			}
	}
	return 0;
}

并查集版本

#include<iostream>
#include<cstring>
#include<map>
#include<string>
#include<vector>
#include<algorithm>
#define maxn 30
using namespace std;
int n, m,g[maxn][maxn],ufs[maxn];
int find(int y)
{
	return ufs[y] < 0 ? y : (ufs[y] = find(ufs[y]));
}
void setUnion(int r1, int r2)
{
	if (r1 == r2) return;
	if (ufs[r1] < ufs[r2])
	{
		ufs[r1] += ufs[r2];
		ufs[r2] = r1;
	}
	else
	{
		ufs[r2] += ufs[r1];
		ufs[r1] = r2;
	}
}
int main()
{
	int kase = 0;
	while (cin>>n>>m && n)
	{
		memset(g, 0, sizeof(g));
		map<string, int> nton;
		map<int,string> rev;
		int ncnt = 0,t1,t2;
		string n1, n2;
		vector<vector<int> > ans;
		for (int i = 0; i < m; i++)
		{
			cin >> n1 >> n2;
			if (nton.count(n1)) t1 = nton[n1];
			else t1 = nton[n1] = ncnt++;
			if (nton.count(n2)) t2 = nton[n2];
			else t2 = nton[n2] = ncnt++;
			g[t1][t2] = 1;
		}
		for (int k = 0; k < n; k++)
			for (int i = 0; i < n; i++)
				for (int j = 0; j < n; j++)
					g[i][j] = g[i][j] || (g[i][k] && g[k][j]);
		for (auto it = nton.begin(); it != nton.end(); it++)
			rev[it->second] = it->first;
		memset(ufs, -1, sizeof(ufs));
		for (int i = 0; i < n; i++)
			for (int j = i + 1; j < n; j++)
				if (g[i][j] && g[j][i])
					setUnion(find(i), find(j));
		for (int i = 0; i < n; i++)
		{
			find(i);//保证ufs[i]就是树根的坐标
			if (ufs[i] < 0) ans.push_back({ i });//每个树根作为联通分量的第一个元素
		}
		for(int i=0;i<n;i++) 
			for(int j=0;j<ans.size();j++)
				if (ans[j][0] == ufs[i])
				{
					ans[j].push_back(i);
					break;
				}
		if (kase) cout << endl;
		printf("Calling circles for data set %d:\n", ++kase);
		for (int i = 0; i < ans.size(); i++)
		{
			for (int j = 0; j < ans[i].size(); j++)
			{
				if (j) cout << ", ";
				cout << rev[ans[i][j]];
			}
			cout << endl;
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值