Time Limit: 1000 MS Memory Limit: 131,072.0 KB
题目描述
我们已知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
题目分析:
这题关键在题目的这一句话:若在Bi和Gi离婚的前提下,这2n个人最终依然能够结合成n对情侣,那么我们称婚姻i为不安全的。
也就是说在某对夫妻离婚的情况下,经过一连串旧情复燃绿帽离婚事件后,如果最终这2n个人每个人依然有自己的配偶,不会有人剩下并且剩下的人相互之间没有旧情(不需要所有人都离了在结,可以存在有的夫妻不受影响),那么就不安全(为什么不安全请细品)
那么什么时候满足上诉条件呢?注意题目所给的关系都是男女之间,没有男男和女女,如果a是男的,和b离婚了,找上了c,那么c原来的配偶d男会去找下一个女的,就这样一直离下去,如果想要最终依然能够结合成n对情侣,那么一定会离到某对夫妻,这对夫妻单出来的男x会和最早单出来的b在一起。所以说只要找到这样一个环就行。
但是如果每次都去找环,根据题意n最大为4000,复杂度太大。然后我们会注意到如果k(k为偶)个人构成一个环,那么这k个人相互之间相互可达,一定在一个强连通分量里,如果k个人在一个强连通分量里,那么相互之间可达一定会找到一个环,但题目不需要我们去找,于是我们可以用tarjan处理出所有人所在的强连通分量,这题就是一道tarjan裸题了。
注意我们连的是单向边,如果夫妻之间连的是女到男的单向边,那么一对夫妻中的男a与另一对夫妻中的女d就要连男到女的单向边,这样才会构成一个环,不然会导致男的只有入度,女的只有出度。
代码:
#include <cstdio>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
const int N = 1e4 + 10;
char a[10], b[10];//先女后男
map<string, int> mp;//将名字映射为数字
vector<int> g[N];
int dfn[N], low[N], s[N], scc[N];
int top, cnt, num;
void tarjan(int u){//模板
s[++ top] = u;
dfn[u] = low[u] = ++ num;
for (int i = 0; i < g[u].size(); ++ i){
int v = g[u][i];
if (!dfn[v]){
tarjan(v);
low[u] = min(low[u], low[v]);
}else if (!scc[v]) low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]){
++ cnt;
while (1){
int v = s[top --];
scc[v] = cnt;
if (u == v) break;
}
}
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; ++ i){
scanf("%s %s", a, b);
mp[a] = 2 * i;
mp[b] = 2 * i + 1;
g[2 * i].push_back(2 * i + 1);//夫妻建立女到男的单向边
}
int m;
cin >> m;
for (int i = 1; i <= m; ++ i){
scanf("%s %s", a, b);
g[mp[b]].push_back(mp[a]);//曾经的情侣建立男到女的单向边
}
for (int i = 0; i < 2 * n; ++ i){
if (!dfn[i]) tarjan(i);//tarjan求强连通分量
}
for (int i = 0; i < n; ++ i){
if (scc[i * 2] == scc[i * 2 + 1]) printf("Unsafe\n");//如果夫妻双方在一个强连通分量里头,则不安全
else printf("Safe\n");
}
return 0;
}