BZOJ 2140 稳定婚姻

这题题面有毒 我就不发了

对于这种毒瘤题我们先配个图

 dong ma zheng gong xue cai bi chi



第一眼上去是个似乎很显然的二分图完美匹配问题

然后算了算复杂度似乎要GG(并没有验证) 于是我们去研究一下Hungary的流程

本题换一种思路 题目中给了你一组完美匹配

又给出了几组连边 其实是问你是否存在另一组完美匹配


对于样例1,我们简化一下


我们把夫妻中女方对男方连一条有向边,前女友中男方对女方连一条边

同比Hungary的流程 这里B1其实是在向G2发出申请匹配

而G2本来的匹配是B2 B2又不存在与B1的原匹配的连边 则本婚姻是稳定的


对于样例2


其实是多了一条B2向G1的连边 而G1 B1 G2 B2形成了一个增广环

所以新匹配是可以构成的


所以我们只需要Tarjan求一下强连通分量 并判断同一对夫妻中的两人是否在同一个强连通分量中即可


Tips:对于人名的处理需要Hash一下或者直接用map,否则要T.


#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
#include <map>
#define travel(u) for(int i = head[u]; i; i = edge[i].next)
using namespace std;
 
const int MAXN = 4000 + 10;
const int MAXM = 20000 + 10;
 
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
 
map <string, int> name;
struct node { int to, next; }edge[MAXM << 1];
int head[MAXN << 1], n, m, cnt;
 
inline void addedge(int u, int v) {
    edge[++cnt].to = v; edge[cnt].next = head[u];
    head[u] = cnt;
}
 
stack<int> S;
int pre[MAXN << 1], lowlink[MAXN << 1], sccno[MAXN << 1];
int dfs_clock, scc_cnt;
 
void dfs(int u, int f) {
    pre[u] = lowlink[u] = ++dfs_clock;
    S.push(u);
    travel(u) {
        int v = edge[i].to;
        if(!pre[v]) {
            dfs(v, u);
            lowlink[u] = min(lowlink[u], lowlink[v]);
        }
        else if(!sccno[v]) {
            lowlink[u] = min(lowlink[u], pre[v]);
        }
    }
    if(lowlink[u] == pre[u]) {
        scc_cnt++;
        for(;;) {
            int x = S.top(); S.pop();
            sccno[x] = scc_cnt;
            if(x == u) break;
        }
    }
}
 
void tarjan() {
    scc_cnt = dfs_clock = 0;
    memset(pre, 0, sizeof(pre));
    memset(sccno, 0, sizeof(sccno));
    for(int i = 1; i <= (n << 1); ++i)
        if(!pre[i]) dfs(i, 0);
}
 
int main() {
    n = read(); char w[20], h[20];
    for(int i = 1; i <= n; ++i) {
        scanf("%s %s", w, h);
        name[w] = i; name[h] = i + n;
        addedge(i, i + n);
    }
    m = read();
    for(int i = 1; i <= m; ++i) {
        scanf("%s %s", w, h);
        addedge(name[h], name[w]);
    }
    tarjan();
    for(int i = 1; i <= n; ++i) {
        if(sccno[i] == sccno[i + n]) puts("Unsafe");
        else puts("Safe");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值