稳定婚姻

本文介绍了一种基于图论的算法,用于判断给定婚姻关系网中每对婚姻的安全性。通过构建二分图并利用Tarjan算法求强连通分量,算法能有效识别可能引发连锁离婚反应的婚姻关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:
我们已知n对夫妻的婚姻状况,称第i对夫妻的男方为Bi,女方为Gi。若某男Bi与某女Gj曾经交往过(无论是大学,高中,亦或是幼儿园阶段,i≠j),则当某方与其配偶(即Bi与Gi或Bj与Gj)感情出现问题时,他们有私奔的可能性。不妨设Bi和其配偶Gi感情不和,于是Bi和Gj旧情复燃,进而Bj因被戴绿帽而感到不爽,联系上了他的初恋情人Gk……一串串的离婚事件像多米诺骨牌一般接踵而至。若在Bi和Gi离婚的前提下,这2n个人最终依然能够结合成n对情侣,那么我们称婚姻i为不安全的,否则婚姻i就是安全的。给定所需信息,你的任务是判断每对婚姻是否安全。

输入:
第一行为一个正整数n,表示夫妻的对数;
以下n行,每行包含两个字符串,表示这n对夫妻的姓名(先女后男),由一个空格隔开;
第n+2行包含一个正整数m,表示曾经相互喜欢过的情侣对数;
以下m行,每行包含两个字符串,表示这m对相互喜欢过的情侣姓名(先女后男),由一个空格隔开。
所有姓名字符串中只包含英文大小写字母,大小写敏感,长度不大于8,
保证每对关系只在输入文件中出现一次,输入文件的最后m行不会出现未在之前出现过的姓名,
这2n个人的姓名各不相同

输出:
输出文件共包含n行,第i行为“Safe”(如果婚姻i是安全的)或“Unsafe”(如果婚姻i是不安全的)。

数据范围:
对于 15% 的数据: 2 <= n <= 20,2 <= m <= 40
对于 40% 的数据: 2 <= n <= 100,2 <= m <= 400
对于 60% 的数据: 2 <= n <= 3500,2 <= m <= 10000
对于 100% 的数据:2 <= n <= 4000 2 <= m <= 20000
所有姓名字符串中只包含英文大小写字母,大小写敏感,长度不大于8

输入样例:
样例输入1:
2
Melanie Ashley
Scarlett Charles
1
Scarlett Ashley
样例输入2:
2
Melanie Ashley
Scarlett Charles
2
Scarlett Ashley
Melanie Charles
样例输入3:
3
Alice Apple
Bob Banana
Celina Cat
2
Alice Banana
Bob Apple

输出样例:
样例输出1:
Safe
Safe
样例输出2:
Unsafe
Unsafe
样例输出3:
Unsafe
Unsafe
Safe

思路:
由题目可看出,这是个二分图,正式的夫妻女向男连边,旧情人男向女连边,如果夫妻二人在同一个强连通分量内,那么找到夫妻男到女的一个环,关系在环上转移一下就离婚了,因此只需要求强连通分量即可。

代码:

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define met(a) memset(a,0,sizeof(a))
using namespace std;
const int mod=1e9+7;
const int N = 4010;
const int MAXN = 10020;
const int MAXM = 50020;
char a[10] , b[10];
struct Edge{
	int to , next;
}edge[MAXM];
int head[MAXN] , tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];
int Index , top;
int scc;
bool Instack[MAXN];
int name[MAXN*2];
int num[MAXN];
int sd = 0;
map<string , int> mp;

void addedge(int u , int v)
{
	edge[tot].to = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}
void Tarjan(int u)
{
	int v;
	Low[u] = DFN[u] = ++Index;
	Stack[top++] = u;
	Instack[u] = true;
	for(int i = head[u] ; i != -1 ; i= edge[i].next)
	{
		v = edge[i].to;
		if(!DFN[v])
		{
			Tarjan(v);
			if(Low[u] > Low[v])
			{
				Low[u] = Low[v];
			}
		}
		else if(Instack[v] && Low[u] > DFN[v])
		{
			Low[u] = DFN[v];
		}
	}
	if(Low[u] == DFN[u])
	{
		scc++;
		do{
			v = Stack[--top];
			Instack[v] = false;
			Belong[v] = scc;
			num[scc]++;
		}
		while(v!=u);
	} 
}
void solve(int N)
{
	memset(DFN , 0 , sizeof(DFN));
	memset(Instack , false , sizeof(Instack));
	memset(num , 0 , sizeof(num));
	Index = scc = top = 0;
	for(int i = 1 ; i <= N ; i++)
	{
		if(!DFN[i])
		{
			Tarjan(i);
//			num[i] = scc;
//			scc = 0;
		}
	}
}
void init()
{
	tot = 0;
	memset(head , -1 , sizeof(head));
}
int main()
{
	int n;
	cin >> n;
	tot = 0;
	init();
	for(int i = 1 ; i <= n ; i++)
	{
		scanf("%s %s",a,b);
		if(!mp[a])
			mp[a] = ++sd;
		if(!mp[b])
			mp[b] = ++sd;
		int xx = mp[a];
		int yy = mp[b];
		name[2*i-1] = xx;
		name[2*i] = yy;
		addedge(xx,yy);
	}
	int m;
	cin >> m;
	for(int i = 1 ; i <= m ; i++)
	{
		scanf("%s %s",a,b);
//		edge[mp[b]].to = mp[a];
//		edge[mp[a]].next = mp[b];
		int xx = mp[a];
		int yy = mp[b];
		addedge(yy,xx);
	}
//	for(int i = 0 ; i < tot ; ++i)
//	{
//		init();
//		if(!DFN[i])
//		{
//			Tarjan(i);
//			num[i] = scc;
//			scc = 0;
//		}
//	}
	for(int i=1;i<=sd;i++){
		if(!DFN[i]) Tarjan(i);
	}
	for(int i = 1 ; i <= n ; ++i)
	{
		if(Low[name[2*i-1]] == Low[name[i*2]] )
			cout << "Unsafe" << endl;
		else
			cout << "Safe" << endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值