51nod 2903 - 稳定婚姻 - Tarjan求强连通分量(SCC)模板题

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;
}
发布了21 篇原创文章 · 获赞 0 · 访问量 830

猜你喜欢

转载自blog.csdn.net/tourist1412/article/details/104884328
今日推荐