舞动的夜晚(二分图的必须边和可行边)

Posted wzhh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了舞动的夜晚(二分图的必须边和可行边)相关的知识,希望对你有一定的参考价值。

题目描述

L公司和H公司举办了一次联谊晚会。晚会上,L公司的N位员工和H公司的M位员工打算进行一场交际舞。在这些领导中,一些L公司的员工和H公司的员工之间是互相认识的,这样的认识关系一共有T对。舞会上,每位员工会尝试选择一名Ta认识的对方公司的员工作为舞伴,并且每位员工至多跳一支舞。完成的交际舞的数量越多,晚会的气氛就越热烈。顾及到晚会的气氛,员工们希望知道,哪些员工之间如果进行了交际舞,就会使整场晚会能够完成的交际舞的最大数量减小。

输入

第一行三个整数N、M、T。
接下来T行每行两个整数x、y,表示L公司的员工x和H公司的员工y互相认识。

输出

第一行一个整数cnt,表示进行了交际舞后会使整场晚会能够完成的交际舞的最大数量减小的员工有多少对。
第二行cnt个整数,升序输出这样的一对员工的认识关系的编号(他们的认识关系是在输入数据中读入的第几条认识关系)。如果cnt=0,输出一个空行

样例输入

3 3 6
1 1
2 1
2 2
3 1
3 2
3 3

样例输出

3
2 4 5

运用网络流求解最大匹配后(不一定是完美匹配)
流量为1的边(匹配边)建从左往右的边
流量为0的边(非匹配边)建从右往左的边
必须边:若(x,y)流量为1且x,y在不同的强连通分量中
可行边:若(x,y)流量为1或(x,y)在同一个强连通分量中

//这道题是求非可行边 
#include<bits/stdc++.h>
using namespace std;
const int N=20200,inf=0x3f3f3f3f;
int n,m,k,s,t;
int cnt=1,fir[N],head[N];
struct node{
    int las,nxt,to,w,id;
}e[N*12];
void add(int u,int v,int w,int id){
    e[++cnt].nxt=fir[u];fir[u]=cnt;e[cnt].to=v,e[cnt].w=w,e[cnt].id=id,e[cnt].las=u;
    e[++cnt].nxt=fir[v];fir[v]=cnt;e[cnt].to=u,e[cnt].w=0,e[cnt].id=id,e[cnt].las=v;
}
queue<int>q;
int dep[N];
bool bfs(){
    q.push(s);
    memset(dep,0x3f,sizeof(dep));
    dep[s]=1;
    int u,v;
    while(!q.empty()){
        u=q.front();
        q.pop();
        for(int i=fir[u];i;i=e[i].nxt){
            v=e[i].to;
            if(dep[v]==inf&&e[i].w>0){
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }   
    }
    return dep[t]<inf;
}
int dfs(int u,int d){
    if(u==t)return d;
    for(int v,i=head[u];i;i=e[i].nxt){
        head[u]=i;
        v=e[i].to;
        if(dep[v]==dep[u]+1&&e[i].w>0){
            int flow=dfs(v,min(e[i].w,d));
            if(flow>0){
                e[i].w-=flow;
                e[i^1].w+=flow;
                return flow;
            }
        }
    }
    return 0;
}
void dinic(){
    int ans=0;
    while(bfs()){
        int flow;
        for(int i=s;i<=t;++i)head[i]=fir[i];
        while(flow=dfs(s,inf))ans+=flow;
    }
//  printf("%d",ans);
}
vector<int>a[N];
bool flag[N*5];
void add_edge(){
    for(int i=2;i<=cnt;i+=2){
        if(e[i].w>0){
            a[e[i].to].push_back(e[i].las);
        }
        else flag[e[i].id]=false,a[e[i].las].push_back(e[i].to);
    }
}
int Index,stk[N],top,dfn[N],low[N];
bool in[N],vis[N];
int col[N],num;
void tarjan(int u){
    dfn[u]=low[u]=++Index;
    stk[++top]=u;
    in[u]=vis[u]=1;
    int v;
    for(int i=0;i<a[u].size();++i){
        v=a[u][i];
        if(!vis[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(in[v])low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]){
        ++num;
        do{
            v=stk[top--];
            in[v]=0;
            col[v]=num;
        }while(dfn[v]!=low[v]);
    }
}
struct Edge{
    int u,v;
}edge[N*5];
int ans,p[N*5];
int main(){
//  freopen("d.in","r",stdin);
    scanf("%d%d%d",&n,&m,&k);
    s=0,t=n+m+1;
    int u,v;
    for(int i=1;i<=n;++i)add(s,i,1,k+1);
    for(int i=1;i<=m;++i)add(i+n,t,1,k+1);
    for(int i=1;i<=k;++i){
        scanf("%d%d",&u,&v);
        edge[i].u=u,edge[i].v=v;
        add(u,v+n,1,i);
        flag[i]=true;
    }
    dinic();
    add_edge(); 
//  for(int i=s;i<=t;++i){
//      for(int j=0;j<a[i].size();++j)
//          printf("%d ",a[i][j]);
//      puts("");
//  }
    for(int i=s;i<=t;++i)
        if(!vis[i])tarjan(i);
//  for(int i=s;i<=t;++i)printf("%d ",col[i]);puts("");
    for(int i=1;i<=k;++i){
        if(col[edge[i].u]==col[edge[i].v+n])//条件2
            flag[i]=false; 
    }
    for(int i=1;i<=k;++i)
        if(flag[i])p[++ans]=i;
    printf("%d
",ans);
    for(int i=1;i<=ans;++i)
        printf("%d ",p[i]);
    return 0;
}

以上是关于舞动的夜晚(二分图的必须边和可行边)的主要内容,如果未能解决你的问题,请参考以下文章

CH#17C 舞动的夜晚(Dinic+tarjan)

二分图最大匹配的可行边和必须边

[Contest Hunter#17-C] 舞动的夜晚

《啊哈算法》——割点割边二分图

算法笔记_139:二分图的最大权分配(Java)

CF553C Love Triangles(二分图)