HDU 3926 Hand in Hand

传送门

无向图的图同构问题(Graph Isomorphism)。所谓图同构就是边和点的几何构型是否一致,不涉及点编号和边权值。

这道题大大简化了这个问题。在这道题中,点的最大度数是2,所以图的构型中每个连通分量要么是链(包括单个点),要么是单环(单环就是指“单重环”,是对一个连通分量的描述;而“简单环”是对一条环路的描述)。

可以想到,每一个连通分量用两个变量来标识:一个是点数;一个区分是链还是环。
对此设定一种排序规则,然后对两个图比较是否全等就完事了。

我一直使用的都是并查集的“根记录个数(负值)版本”。
C++的pair自带的operator<函数,对基本类型按照字典序比较。

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <utility>
using namespace std;

const int MAXN = 10001;
int N, M, T;
int pre[2][MAXN];
bool flag[2][MAXN];           // 仅对根节点生效,区分该连通分量是不是环
vector<pair<int, int>> v[2];

void init()
{
	memset(pre, -1, sizeof pre);
	memset(flag, 0, sizeof flag);
	for (int i = 0; i < 2; i++)
		v[i].clear();
}

int f(int x, int id)
{
	if (pre[id][x] < 0) return x;
	return pre[id][x] = f(pre[id][x], id);
}

bool u(int n1, int n2, int id)
{
	int f1 = f(n1, id);
	int f2 = f(n2, id);
	if (f1 != f2)
	{
		if (pre[id][f1] <= pre[id][f2])
		{
			pre[id][f1] += pre[id][f2];
			pre[id][f2] = f1;
		}
		else
		{
			pre[id][f2] += pre[id][f1];
			pre[id][f1] = f2;
		}
		return true;
	}
	return false;
}

int main()
{
	int a, b;
	scanf("%d", &T);
	for (int num = 1; T--; num++)
	{
		init();
		for (int t = 0; t < 2; t++)
		{
			scanf("%d%d", &N, &M);
			for (int i = 0; i < M; i++)
			{
				scanf("%d%d", &a, &b);
				if (!u(a, b, t))                  // 有环,这个连通分量已经定死了,可以立即加入vector
				{
					v[t].push_back(make_pair(-pre[t][f(a, t)], 0));
					flag[t][f(a, t)] = true;
				}
			}
			for (int i = 1; i <= N; i++)
			{
				if (pre[t][i] < 0 && !flag[t][i])
					v[t].push_back(make_pair(-pre[t][i], 1));
			}
			sort(v[t].begin(), v[t].end());       // pair自动按照字典序排序,相同个数的连通分量,环都在前面  
		}

		if (v[0].size() != v[1].size())
		{
			printf("Case #%d: NO\n", num);
			continue;
		}
		for (int i = 0; i < v[0].size(); i++)
		{
			if (v[0][i] != v[1][i])
			{
				printf("Case #%d: NO\n", num);
				break;
			}
			if (i == v[0].size() - 1)
				printf("Case #%d: YES\n", num);
		}
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值