二分图似乎是很多图论问题中能得部分分的办法
学号二分图,部分分到手
基本概念
- 定义:节点由两个集合组成,且两个集合内部没有边的图。
- 链表、树都属于二分图。
其中,链表按序数奇偶分为两个子集,树按深度奇偶分为两个子集 - 完全二分图:如果点集X中的任一顶点与点集Y中每个顶点均有且仅有唯一的一条边相连,则称G为完全二分图或完全偶图。
完全二分图建模主要用于一些指派问题的最优化。 - 二分图不存在长度为奇数的环。
二分图判定
染色冲突法
#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;
}
创造二分图
- 有环才有意思,没环怎么搞都是二分图
- 偶环是二分图,奇环不成为二分图
- 所以搜奇环,破坏奇环
二分图匹配
- 定义:设 G G G为二分图,若在 G G G的子图 M M M中,任意两条边都没有公共节点,那么称 M M M为二分图 G G G的一个匹配,且 M M M的边数为匹配数。
- 完美匹配: M M M取完 G G G两个点集中更小的一个且为 G G G的一个匹配
- 匈牙利算法求最大匹配(边数与点数都是最大的,想想这个道理)
后来居上原则(可怜那个男生)
板子
#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算法,不过我没看懂那是什么