二分图基础

二分图似乎是很多图论问题中能得部分分的办法
学号二分图,部分分到手

基本概念

  1. 定义:节点由两个集合组成,且两个集合内部没有边的图。
  2. 链表、树都属于二分图。
    其中,链表按序数奇偶分为两个子集,树按深度奇偶分为两个子集
  3. 完全二分图:如果点集X中的任一顶点与点集Y中每个顶点均有且仅有唯一的一条边相连,则称G为完全二分图或完全偶图。
    完全二分图建模主要用于一些指派问题的最优化。
  4. 二分图不存在长度为奇数的环。

二分图判定

染色冲突法

#include<bits/stdc++.h>
#define in Read()
#define re register
inline int in{
	int i=0,f=1;char ch;
	while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
	if(ch=='-')f=-1,ch=getchar();
	while(ch<='9'&&ch>='0')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}
const int NNN=1e6+10;
int n,m;
int tot,first[NNN],next[NNN<<1],aim[NNN<<1],colour[NNN];
bool vis;

inline void Add(int u,int v){
	next[++tot]=first[u];
	first[u]=tot;
	aim[tot]=v;
}

inline bool Dfs(int now,int c){
	colour[now]=c;
	for(re int e=first[now];e;e=next[e]){
		if(colour[aim[e]]==c)return false;
		if(colour[aim[e]]==0&&!Dfs(aim[e],-c))return false;
	}
	return true;
}

int main(){
	while(~scanf("%d%d",&n,&m)){
		tot=0,vis=true;
		memset(first,0,sizeof(first));
		memset(next,0,sizeof(next));
		memset(aim,0,sizeof(aim));
		memset(colour,0,sizeof(colour));
		for(re int i=1;i<=m;++i){
			int u=in,v=in;
			Add(u,v);
			Add(v,u);
		}
		for(re int i=1;i<=n;++i)if(!colour[i])vis&=Dfs(i,1);
		if(vis)printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

创造二分图

  • 有环才有意思,没环怎么搞都是二分图
  • 偶环是二分图,奇环不成为二分图
  • 所以搜奇环,破坏奇环

二分图匹配

  1. 定义:设 G G G为二分图,若在 G G G的子图 M M M中,任意两条边都没有公共节点,那么称 M M M为二分图 G G G的一个匹配,且 M M M的边数为匹配数。
  2. 完美匹配: M M M取完 G G G两个点集中更小的一个且为 G G G的一个匹配
  3. 匈牙利算法求最大匹配(边数与点数都是最大的,想想这个道理)
    后来居上原则(可怜那个男生)
    板子
#include<bits/stdc++.h>
#define in Read()
#define re register
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
	if(ch=='-')f=-1,ch=getchar();
	while(ch<='9'&&ch>='0')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}
const int NNN=2e3+10;
int n,m,q,cnt;
//int tot,first[NNN],next[NNN<<1],aim[NNN<<1];
vector<int>G[NNN];
int used[NNN];int match[NNN];
//inline void Add(int u,int v){next[++tot]=first[u];first[u]=tot;aim[tot]=v;}

inline bool DFS(int u){
//	for(re int e=first[u];e;e=next[e]){
	int len=G[u].size();
	for(re int i=0;i<len;++i){
//		int v=aim[e];
		int v=G[u][i];
		if(used[v])continue;
		used[v]=1;
		if(!match[v]||DFS(match[v])){
			match[v]=u;
			match[u]=v;
			return 1;
		}
	}
	return 0;
}

int main(){
	n=in,m=in,q=in;
	for(re int i=1;i<=q;++i){
		int u=in,v=in;
		if(v>m||u>n)continue;
//		Add(u,v+n);
//		Add(v+n,u);
		G[u].push_back(v+n);
		G[v+n].push_back(u);
	}
	for(re int i=1;i<=n;++i){
		memset(used,0,sizeof(used));
//		for(re int j=1;j<=n+m;++j)used[j]=false;
		if(DFS(i)) ++cnt;
	}
	printf("%d\n",cnt);
	return 0;
}

感谢@gigo指出错误
边数是n*m的我却只开了n+m

练习POJ2536

#include<bits/stdc++.h>
#define in Read()
#define re register
inline int in{
	int i=0,f=1;char ch;
	while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
	if(ch=='-')f=-1,ch=getchar();
	while(ch<='9'&&ch>='0')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}
const int NNN=1001;
const int MMM=10001;
int n,m,s,v,d;
struct PNT{
	double x,y;
	PNT(){}
	PNT(double _x,double _y){x=_x,y=_y;}
	inline void SCAN_PNT(){scanf("%lf%lf",&x,&y);}
	inline void PRINT_PNT(){printf("%lf %lf\n",x,y);}
	friend inline PNT operator - (const PNT &u,const PNT &v){return PNT(u.x-v.x,u.y-v.y);}
	friend inline double operator * (const PNT &u,const PNT &v){return u.x*v.x+u.y*v.y;}
	friend inline double dist(PNT u,PNT v){return sqrt((u-v)*(u-v));}
}mouse[NNN],hole[NNN];
int tot,first[MMM],next[MMM<<1],aim[MMM<<1];
inline void Add(int u,int v){++tot;next[tot]=first[u];first[u]=tot;aim[tot]=v;}
bool used[NNN];
int match[NNN<<1],cnt;

inline bool Dfs(int u){
	for(re int e=first[u];e;e=next[e]){
		int v=aim[e];
		if(used[v])continue;
		used[v]=true;
		if(!match[v]||Dfs(match[v])){
			match[v]=u;
			match[u]=v;
			return true;
		}
	}
	return false;
}

void solve(){
	d=s*v;
	for(re int i=1;i<=n;++i)mouse[i].SCAN_PNT();
	for(re int i=1;i<=m;++i)hole[i].SCAN_PNT();
	
	for(re int i=1;i<=n;++i)
		for(re int j=1;j<=m;++j)
			if(dist(mouse[i],hole[j])<=d)
				Add(i,j+n),Add(j+n,i);
	
	for(re int i=1;i<=n;++i){
		memset(used,0,sizeof(used));
		if(Dfs(i))cnt++;
	}
	
	printf("%d\n",n-cnt);
}

int main(){
	while(~scanf("%d%d%d%d",&n,&m,&s,&v)){
		memset(mouse,0,sizeof(mouse));
		memset(hole,0,sizeof(hole));
		memset(first,0,sizeof(first));
		memset(next,0,sizeof(next));
		memset(aim,0,sizeof(aim));
		memset(match,0,sizeof(match));
		cnt=0,tot=0;
		solve();
	}
	return 0;
}

这题。。。
poj里面的竟然有多组数据。。。
数组一开大,AC手里抓。。。233333333

一些奇奇怪怪的结论

二分图的最小点覆盖=最大匹配(点集覆盖所有边)
二分图的最小边覆盖=顶点数-最大匹配数(边集覆盖所有点)

二分图的最大点独立集=顶点数-最大匹配数(点集中的所有点互不相连)
二分图的最大边独立集=最大匹配(所有边没有共同顶点)

这些结论都挺显然的,如果遇到意思意思就懂起了

学案后面还有一个KM算法,不过我没看懂那是什么

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值