题解 P1407 【[国家集训队]稳定婚姻】

这是蒟蒻的第三篇题解。

这道题就是绿与被绿的故事。

在读懂题以后,我们可以归纳出,如果这段姻缘是Unsafe的,那么这么几个人的关系必须是是个环。

什么意思呢?

以样例2为例:

tijie1.png

假设奇数的是 male ,偶数的是 female 。

1 2 3 4分别代表 Melanie Ashley Scarlett Charles.

那么在如图这个环中:原本的夫妻是12和34,但是由于他们在一个环中,说白了,就是在这个强连通分量中,他们可以任意更换对象。(狗血)

样例1不必多说,(三角恋是不会有好结果的),因为这三个人没有构成强连通分量,所以婚姻安全了。(实际上就是把上图的1-4边断掉)

所以就很明显,我们只需要建好图,然后运用 tarjan 求解强连通分量,如果一对夫妻在同一个强连通分量里面,那么婚姻就有危机了,否则就会风平浪静。

然后又有一个问题了:怎么建图。

原题读完后,很容易想到无向图,即建双边。

但是!!!

如果这样的话,我们上面所推导的一切全都前功尽弃了,因为无向图求强连通分量\(==\)想peach.强连通分量是针对有向图来说的。

所以我们在建图时,要把夫妻和情侣都拆开建图。

那怎么拆才能形成环呢?

有三种方案:

  1. 男 -> 女
  2. 女 -> 男
  3. 男 -> 女 和 女 -> 男分开

很显然,第一种和第二种是无论如何也不会出现环的。

还是以样例2为例,如果按第1种方式建图,那么会是这样:

tijie2.png

这就很毒瘤。

第2种同理。

再看第三种:建图结果就像最上面我刚开始讲时用的那幅图。

我再放一遍这个图:

tijie1.png

我是将夫妻间用 女 -> 男 存图,情侣间用 男 -> 女 存图。(内部含义自行理解 /kk)

这样的话,就能通过女 -> 男 -> 女 -> 男来用算法找到环。

对了,不会\(tarjan\)出门右转不谢。

这道题实际上只要弄明白怎么建图,就是一道tarjan 模板题

讲了这么一大堆,下面是喜闻乐见的代码时间:

#include<bits/stdc++.h>
using namespace std;

int cnt,n,tail,m,h[8005],dfn[8005],low[8005],id,tarn,bl[8005],x[8005],y[8005];
bool vis[8005];
stack<int> s;
map<string,int> to;//map映射,将名字点映射成数字点 

struct node {//链式前向星存图
    int to,nxt;
} b[25005];

void add(int x,int y) {//加边操作 
    b[++cnt].to=y;
    b[cnt].nxt=h[x];
    h[x]=cnt;
}

void tarjan(int u) {//tarjan算法求强联通分量 
    s.push(u);
    vis[u]=1;
    dfn[u]=low[u]=++id;
    for(int i=h[u]; i; i=b[i].nxt) {
        int v=b[i].to;
        if(!dfn[v]) {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        } else {
            if(vis[v]) {
                low[u]=min(low[u],dfn[v]);
            }
        }
    }
    if(dfn[u]==low[u]) {
        tarn++;
        int v;
        do {
            v=s.top();
            s.pop();
            bl[v]=tarn;
            vis[v]=0;
        } while(u!=v);
    }
}

int main() {
    cin>>n;
    string a,b;
    for(int i=1; i<=n; i++) {
        cin>>a>>b;
        x[i]=to[a]=++tail;//将女方转换成数字
        y[i]=to[b]=++tail;//将男方转换成数字
        add(to[a],to[b]);//female -> male 存边
    }
    cin>>m;
    for(int i=1; i<=m; i++) {
        cin>>a>>b;
        add(to[b],to[a]);//male -> female 存边
    }
    for(int i=1; i<=tail; i++) {
        if(!dfn[i]) {
            tarjan(i);
        }
    }
    for(int i=1; i<=n; i++) {
        if(bl[x[i]]==bl[y[i]]) {//bl 即 belong 表示当前这个点属于哪一个强连通分量 
            puts("Unsafe");
        } else {
            puts("Safe");
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ahawzlc/p/12482162.html