BZOJ-2140: 稳定婚姻(网络流+Tarjan算法求强连通分量)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2140

思路:

很明显的二分图匹配的模型题(首先每个人看成一个点,男女分别分成两个集合,关系用点之间的连边来表示即可),对于每对婚姻关系是否稳定,其实可以转化为二分图中对应的边是否为关键匹配边(即删去该边之后最大匹配数是否仍然是n),如果是关键匹配边就是“Safe”,否则就是"Unsafe"。

对于求关键匹配边,明显直接删边然后跑最大匹配是行不通的,即使是最快的二分图匹配算法也要达到O(nmsqrt(n))的复杂度。所以,我们只能从其他方面入手:首先用网络流求一次最大匹配,然后在残量网络中用Tarjan算法求强连通分量,如果对于一个匹配边(s,t),有s,t在同一个SCC(或者是该边未满流),那么这条边就不是最大匹配边,反之就是。(因为如果在同一SCC中,这条匹配边就可以用该边所在SCC中另外一条匹配边代替。)复杂度O(n^2 m+n+m)(虽然这个复杂度比上面的****O(nmsqrt(n))高不少,但是由于网络流算法sap虽然复杂度高达O(n^2 m),但是优化后在实践中却相当快,与HLPP相差不了多少,所以后者的速度其实比前者快很多,可以解决该题****)****

****代码(代码较长,我在处理名字方面凶残的用了Trie。。。):****

#include <cstring>

#include <string>

#include <cstdlib>

#include <cstdio>

#include <algorithm>

#include <stack>

#include <vector>

 

using namespace std;

 

#define MAXN 8010

#define inf 0x7fffffff

 

struct edge{

    int t,f;

    edge *next,*pair;

    edge (){

        next=pair=NULL;

    }

}*head[MAXN];

 

void Add(int s,int t,int f){

    edge *p=new(edge);

    p->t=t,p->f=f,p->next=head[s];

    head[s]=p;

}

 

void AddEdge(int s,int t,int f){

    Add(s,t,f),Add(t,s,0);

    head[s]->pair=head[t],head[t]->pair=head[s];

}

 

int n,m,V=0,S,T;

int Couple[MAXN][2];

string s1,s2;

edge *E[MAXN];

 

string getstring(){

    string s="";

    int c;

    for(c=getchar();!((c>=int('a')&&c<=int('z'))||(c>=int('A')&&c<=int('Z')));c=getchar());

    s=s+char(c);

    for(c=getchar();(c>=int('a')&&c<=int('z'))||(c>=int('A')&&c<=int('Z'));c=getchar()) s=s+char(c);

    return s;

}

 

struct node{

    node *child[52];

    int v;

    node (){

        memset(child,0,sizeof(child));

    }

}*roof=NULL;

 

int Trans(char c){

    if(c>='a'&&c<='z')return int(c)-int('a');

    return int(c)-int('A')+26;

}

 

int Insert(string c,int p,node*&t){

    if(!t) t=new(node);

    if(p==c.size())return t->v=++V;

    return Insert(c,p+1,t->child[Trans(c[p])]);

}

 

int Search(string c,int p,node *t){

    if(p==c.size())return t->v;

    return Search(c,p+1,t->child[Trans(c[p])]);

}

 

void INIT(){

    memset(head,0,sizeof(head));

    scanf("%d",&n);

    for(int i=0;i++<n;){

        s1=getstring();

        s2=getstring();

        Couple[i][0]=Insert(s1,0,roof);

        Couple[i][1]=Insert(s2,0,roof);

    }

    S=++V;T=++V;

    for(int i=0;i++<n;){

        AddEdge(Couple[i][0],Couple[i][1],1);

        E[i]=head[Couple[i][0]];

        AddEdge(S,Couple[i][0],1),AddEdge(Couple[i][1],T,1);

    }

    scanf("%d",&m);

    while(m--){

        int v1,v2;

        s1=getstring();

        s2=getstring();

        v1=Search(s1,0,roof),v2=Search(s2,0,roof);

        AddEdge(v1,v2,1);

    }

}

 

int h[MAXN],gap[MAXN];

edge *d[MAXN];

 

int sap(int v,int flow){

    if(v==T)return flow;

    int rec=0;

    for(edge *p=d[v];p;p=p->next)if(h[v]==h[p->t]+1&&p->f){

        int ret=sap(p->t,min(flow-rec,p->f));

        p->f-=ret,p->pair->f+=ret;

        d[v]=p;

        if((rec+=ret)==flow)return flow;

    }

    if(!(--gap[h[v]])) h[S]=T;

    gap[++h[v]]++;

    d[v]=head[v];

    return rec;

}

 

void maxflow(){

    memset(h,0,sizeof(h));

    memset(gap,0,sizeof(gap));

    gap[0]=T;

    for(int i=0;i++<T;) d[i]=head[i];

    while(h[S]<T)sap(S,inf);

}

 

int dfn[MAXN],low[MAXN],Index=0,Scc=0,Id[MAXN];

bool f[MAXN];

stack<int>St;

 

void Tarjan(int v){

    dfn[v]=low[v]=++Index;

    St.push(v),f[v]=true;

    for(edge *p=head[v];p;p=p->next)if(p->f){

        if(!dfn[p->t]){

            Tarjan(p->t);

            low[v]=min(low[v],low[p->t]);

        }else{

            if(f[p->t]){

                low[v]=min(low[v],low[p->t]);

            }

        }

    }

    if(low[v]==dfn[v]){

        Scc++;

        int u;

        do{

            u=St.top();

            St.pop();

            f[u]=false;

            Id[u]=Scc;

        }while(u!=v);

    }

}

 

void Solve(){

    memset(dfn,0,sizeof(dfn));

    memset(f,false,sizeof(f));

    while(!St.empty()) St.pop();

    for(int i=0;i++<T;)if(!dfn[i])Tarjan(i);

    for(int i=0;i++<n;){

        if(Id[Couple[i][0]]==Id[Couple[i][1]]||E[i]->f)printf("Unsafe\n")

        ;  else printf("Safe\n");

    }

}

 

int main(){

    INIT();

    maxflow();

    Solve();

    return 0;

}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容