二分图总结【acwing算法提高课】

性质

一个图是二分图,图中不存在奇数环,染色法

关押罪犯

image-20200825144305814

最优解越大,图中右半部分越是二分图

染色法求二分图,这些图不一定联通!!

没有必要每次cheak都建图,在染色中增加判断权重就好了

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
int h[N],ne[N],e[N],w[N],idx,rh[N];
void add(int a,int b,int c)
{
    e[idx]=b;
    ne[idx]=h[a];
    w[idx]=c;
    h[a]=idx++;
}
int n,m;
int colur[N];

bool dfs(int mid,int u,int c)
{
    colur[u]=c;
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(j==u) continue;
        if(w[i]>mid)
        {
            if(colur[j]==0)
                if(!dfs(mid,j,-c))
                    return false;
            if(colur[j]==c)
                return false;
        }
    }
    return true;
}
bool cheak(int mid)
{
    memset(colur,0,sizeof colur);
    for(int i=1;i<=n;i++)
        if(colur[i]==0)
        {
            if(!dfs(mid,i,1)) 
                return false;
        }
    return true;
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=0;i<m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
        add(b,a,c);
    }
    int l=0,r=0x3f3f3f3f;
    while(l<r)
    {
        int mid=l+r>>1;
        if(cheak(mid)) r=mid;
        else l=mid+1;
       
    }
    cout<<r<<endl;
    return 0;
}

棋盘覆盖

奇偶格二分图

把所有可以放的格子连一条边,在图中跑匈牙利算法

主要是维护match匹配数组,st标记数组,不太需要真的连边

代码

#include<bits/stdc++.h>
using namespace std;
const int N=110;
typedef pair<int,int>pll;
pll match[N][N];

int g[N][N];
int n,m;
int st[N][N];
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};

bool find(int a,int b)
{
    for(int i=0;i<4;i++)
    {
        int na=a+dx[i];
        int nb=b+dy[i];
        if(g[na][nb]) continue;
        if(st[na][nb]) continue;
        st[na][nb]=1;
        if(na<1||na>n||nb<1||nb>n) continue;
        if(match[na][nb].first==0 || (find(match[na][nb].first,match[na][nb].second)))
        {
            match[na][nb]={a,b};
            return true;
        }
        
            
    }
    return false;
}
int main()
{
    cin>>n>>m;
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        g[a][b]=1;
    }
    int res=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(g[i][j]==1) continue;
            if(i+j&1)
            {
                memset(st,0,sizeof st);
                if(find(i,j))
                    res++;
            }
        }
    }
    cout<<res<<endl;
    return 0;
}

最小点覆盖

一条边都选一个点

在二分图中,最小覆盖数=最大匹配数

机器匹配

转化为求二分图的最小点覆盖

直接匈牙利算法

最大独立集

选择最多的点,保证这些点内部没有边。

在二分图中

最大独立集

= 去掉最少的点,把所有边破坏掉

= 去掉最小点覆盖

骑士放置

放置最多的骑士,保证互相不攻击到

奇偶二分图之后,选择最多的点,保证骑士之间没有边(独立起来)

= 去掉最少的点,这些点是最大点覆盖

代码

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int N=200;
typedef pair<int,int>pll;
pll match[N][N];
int st[N][N],g[N][N];
int n,m;

int dx[8] = {-2, -2, 2, 2, -1, -1, 1, 1};
int dy[8] = {-1, 1, -1, 1, -2, 2, -2, 2};
bool find(int x,int y)
{
	for(int i=0;i<8;i++)
	{
		int a=x+dx[i];
		int b=y+dy[i];
		if(a<1 || a> n|| b<1 || b>m) continue;
		if(g[a][b])continue;
		if(st[a][b]) continue;
		st[a][b]=1;
		if(match[a][b].first==-1 || find(match[a][b].first,match[a][b].second))
		{
			match[a][b]=make_pair(x,y);
			return true;
		}
	}
	return false;
}
int main()
{
	int t;
	
	cin>>n>>m>>t;
	int tt=t;
	while(t--)
	{
		int a,b;
		cin>>a>>b;
		g[a][b]=1;
	}
	int res=0;
	memset(match,-1,sizeof match);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(g[i][j]==0 && (i+j)%2)
			{
				memset(st,0,sizeof st);
				if(find(i,j))
					res++;
			}
		}
	}
	cout<<n*m-tt-res<<endl;
	return 0;
}

最小路径点覆盖

最少的互不相交路径,把所有点覆盖住

image-20200825171446276

这个图是一个二分图,最少路径点覆盖=n-最大匹配数

最小路径重复点覆盖

先传递闭包,然后求最小路径点覆盖

代码

#include<bits/stdc++.h>
using namespace std;
const int N=500;
int match[N];
int st[N];
int g[N][N];
int n,m;
void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                g[i][j] |= g[i][k] & g[k][j];
    

    
}

bool find(int x)
{
    for(int i=1;i<=n;i++)
    {
        if(st[i]) continue;
        
        if(g[x][i]==0) continue;
        st[i]=1;
        if(!match[i] || find(match[i]))
        {
            match[i]=x;
            return true;
        }
    }
    return false;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a,b;
        cin>>a>>b;
        g[a][b]=1;
        //g[b][a]=1;
    }
    floyd();
    int res=0;
    for(int i=1;i<=n;i++)
    {
        memset(st,0,sizeof st);
        if(find(i))
            res++;
    }
    cout<<n-res<<endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值