二分图匹配的判断+p1322+二分图最大匹配+tyvj1035

 

  二分图就是一个节点数>=2的图如果能被分成左右非空的两部分,且同一集合内部的边不相连,那么就是一个二分图.一张图是二分图,当且仅当图中不存在点数为奇数的环.因为链都好说,而环内相邻的点一定都不在同一个部分,但是点数为奇数时就会有点不能找到自己的位置...

  因为两个部分是等价的,对于一个图直接从一个节点出发dfs即可,复杂度n+m.

using namespace std;
bool dfs(int now){
    int tv=v[now];
    for(int j=head[now];j;j=e[j].next){
        if(v[e[j].y])
            if(v[e[j].y]+tv==3)
                continue;
            else
                return 0;
        else{    
            v[e[j].y]=3%tv;
            if(!dfs(e[j].y))return 0;
        }
        
    }
    return 1;
}
int main()
{
    init();
    memset(v,0,sizeof(v));
    for(i=1;i<=n;i++)
    {
        if(!v[i])
        {    
            v[i]=1;
            if(!dfs(i))
                break;
        }
    }
    if(i==n+1)
        cout<<"yes";
    else
        cout<<"no";
}
模板

  那么来看一个题吧:

       点我跳转

  并查集大概用的也是二分图的思想,考虑如何只用判断二分图的思想来做这道题.

  考虑到答案具有单调性,答案ans及更大的影响力都是可以达到的,更低的影响力则不能达到.那么可以开始二分答案了,其中的check()函数检查影响力最大为多少多少时能否构成二分图.具体实现只需要可以在那个dfs函数里向旁边dfs的时候加一句边长与当前边的比较即可.复杂度(m+n)log21e9.

using namespace std;
int i,xx,yy,vv;
int n,m,tot,head[20010],v[20010];
int l,r,mid;
struct edge{
    int x,y,v;
    int next;
}e[200010];
void add(int x,int y,int v){
    tot++;
    e[tot].x=x;
    e[tot].y=y;
    e[tot].v=v;
    e[tot].next=head[x];
    head[x]=tot;
    return ;
}
bool dfs(int now,int vnow){
    int tv=v[now];
    for(int j=head[now];j;j=e[j].next){
        if(e[j].v<=vnow)continue;
        if(v[e[j].y])
            if(v[e[j].y]+tv==3)
                continue;
            else
                return 0;
        else{   
            v[e[j].y]=3-tv;
            if(!dfs(e[j].y,vnow))return 0;
        }
        
    }
    return 1;
}
bool check(int now){
    memset(v,0,sizeof(v));
    for(i=1;i<=n;i++){
        if(!v[i]){
            v[i]=1;
            if(!dfs(i,now))return 0;
        }
    }
    return 1;
}
int main(){
    n=read();m=read();
    for(i=1;i<=m;i++){
        xx=read();yy=read();vv=read();
        add(xx,yy,vv);
        add(yy,xx,vv);
        r=max(r,vv);
    }
    while(l+1!=r){
        mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else           l=mid;
    }
    if(check(l))cout<<l;
    else        cout<<r;
    return 0;
}
View Code

  虽然看起来没有并查集优秀,但是总时间却比并查集算法快...

  

  "任意两条边都没有公共端点"的边的集合被称为图的一组匹配,在二分图中,包含边数最多的一组匹配被称为二分图的最大匹配.(算法竞赛进阶指南p394).

  由于性质:二分图的一组匹配是最大匹配当且仅当图中不存在增广路.这样就可以用匈牙利算法(增广路算法)求一个二分图的最大匹配了.复杂度(MN).

bool dfs(int now){
    for(int j=head[now];j;j=e[j].next){
        if(!v[e[j].y]){
            v[e[j].y]=1;
            if(!match[e[j].y]||dfs(match[e[j].y])){
                match[e[j].y]=now;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    init();
    for(i=n;i<=n*n+n-1;i++){
        memset(v,0,sizeof(v));
        if(dfs(i))ans++;
    }
}
匈牙利算法

  看一道题:来自http://www.tyvj.cn/p/1035

  再来一个样例吧:

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

  再画一下图:

  青色是被禁止放置的点,红色是要放骨牌的地方.可以看出来答案是5.首先建图,然后跑一下增广路即可.匈牙利具体是怎么跑的呢?它每次从一个点进入dfs看相连的节点有没有还没有被匹配的点.如果有就可以利用它进行增广,并且返回1,或者去dfs相邻的点,如果它返回了1也可以用它进行增广.

using namespace std;
int i,f;
int n,sum,ans,match[1111];//最大为n*n+n-1
struct edge{
    int x,y;
    int next;
}e[80010];//点数*方向数*双向n^2*4*2
int tot,head[1111],v[1111],map[1111][1111];
void add(int x,int y){
    tot++;
    e[tot].x=x;
    e[tot].y=y;
    e[tot].next=head[x];
    head[x]=tot;
    tot++;
    e[tot].x=y;
    e[tot].y=x;
    e[tot].next=head[y];
    head[y]=tot;
    return ;
}
bool dfs(int now){
    for(int j=head[now];j;j=e[j].next){
        if(!v[e[j].y]){
            v[e[j].y]=1;
            if(!match[e[j].y]||dfs(match[e[j].y])){
                match[e[j].y]=now;
                return 1;
            }
        }
    }
    return 0;
}
int main(){
    n=read();sum=read();
    for(;sum;sum--)
        map[read()][read()]=1;//如果你include<map>或者万能库,map就是关键字
    for(i=1;i<=n;i++){//建图
        for(f=1;f<=n;f++){
            if(map[i][f])continue;
            if(!map[i][f-1]&&f!=1)
                add(i*n+f-1,i*n+f-2);
            if(!map[i][f+1]&&f!=n)
                add(i*n+f-1,i*n+f);
            if(!map[i-1][f]&&i!=1)
                add(i*n+f-1,i*n+f-1-n);
            if(!map[i+1][f]&&i!=n)
                add(i*n+f-1,i*n+f-1+n);
        }
    }
    for(i=n;i<=n*n+n-1;i++){//跑匈牙利
        memset(v,0,sizeof(v));
        if(dfs(i))ans++;
    }
    cout<<ans/2;//ans是最大匹配中的点数,而每两个点能放一个骨牌
    return 0;
}
没有地方提交,只是过了样例而已

 

posted @ 2019-01-05 06:39  zzuqy  阅读(230)  评论(0编辑  收藏  举报