二分图:图中点集可分成两部分,使处于同一部分的点间没有连边
染色法,是一种简单地判断是否为二分图的方法.
对图中的每个点进行染色,使相临的点颜色不同,如果可以完成则为二分图,否则不为.
BFS实现:
#include
#include
#include
#include
#include
#include
using namespace std;
int n,m,ru,rv,tot;
int first[100010],nxt[100010],color[100010];
bool flag;
struct edge
{
int u,v;
}l[100010];
queue<int>q;
void build(int f,int t)
{
l[++tot]=(edge){f,t};
nxt[tot]=first[f];
first[f]=tot;
}
int bfs(int s)
{
q.push(s);
color[s]=1;
while(!q.empty())
{
int k=q.front();
q.pop();
for(int i=first[k];i!=-1;i=nxt[i])
{
int x=l[i].v;
if(color[x]==-1)
{
q.push(x);
color[x]=color[k]^1;//color[x]=!color[k];
}
if(color[x]==color[k])
return 0;
}
}
return 1;
}
int main()
{
memset(first,-1,sizeof(first));
memset(color,-1,sizeof(color));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&ru,&rv);
build(ru,rv);
build(rv,ru);
}
for(int i=1;i<=n;i++)//遍历每一个点
{
if(color[i]==-1&&first[i]!=-1)//若起始点没有染过色(此点及此点可到达的点未被染色)并且起始点有出边
{
if(!bfs(i))
flag=1;
}
}
if(flag==1)
printf("NO");
else printf("YES");
return 0;
}
DFS实现:
http://acm.hdu.edu.cn/showproblem.php?pid=5285 wyh2000 and pupil
poor wyh…
#include
#include
#include
#include
#include
using namespace std;
int n,m,ru,rv,tot,zero,one,t,ans1,ans2;
int first[200010],nxt[200010],color[200010];
bool flag;
struct edge
{
int u,v;
}l[200010];
void build(int f,int t)
{
l[++tot]=(edge){f,t};
nxt[tot]=first[f];
first[f]=tot;
}
void dfs(int s)
{
for(int i=first[s];i!=-1;i=nxt[i])
{
int x=l[i].v;
if(color[x]==color[s])
flag=1;
if(color[x]!=-1)
continue;
if(color[x]==-1)
{
color[x]=color[s]^1;
if(color[x]==1)
one++;
else zero++;
}
dfs(x);
}
}
int main()
{
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
tot=0;
memset(first,-1,sizeof(first));
memset(color,-1,sizeof(color));
scanf("%d%d",&n,&m);
if(n<=1)
{
printf("Poor wyh\n");
continue;
}
if(m==0)
{
printf("%d 1\n",n-1);
continue;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&ru,&rv);
build(ru,rv);
build(rv,ru);
}
ans1=ans2=flag=0;
for(int i=1;i<=n;i++)
{
if(color[i]==-1&&first[i]!=-1)
{
one=1;
zero=0;
color[i]=1;
dfs(i);
ans1+=max(one,zero);
ans2+=min(one,zero);
}
if(color[i]==-1&&first[i]==-1)
ans1++;
}
if(flag==1)
printf("Poor wyh\n");
else printf("%d %d\n",ans1,ans2);
}
return 0;
}
二分图匹配算法:匈牙利算法
https://www.luogu.org/problem/show?pid=3386 二分图匹配模板
#include
#include
#include
#include
#include
using namespace std;
int n,m,e,ru,rv,ans,tot;
int x[100010],y[100010],first[500010],nxt[500010];
bool vis[100010];
struct edge
{
int u,v;
}l[500010];
void build(int f,int t)
{
l[++tot]=(edge){f,t};
nxt[tot]=first[f];
first[f]=tot;
}
int path(int k)
{
for(int i=first[k];i;i=nxt[i])
{
int t=l[i].v;
if(!vis[t])
{
vis[t]=1;
if(!y[t]||path(y[t]))//若t没有匹配或与t匹配的点y[t]可以寻找一条增广路与其他的点进行匹配
{
x[k]=t;//匹配
y[t]=k;
return 1;
}
}
}
return 0;
}
int main()
{
scanf("%d%d%d",&n,&m,&e);
for(int i=1;i<=e;i++)
{
scanf("%d%d",&ru,&rv);
if(ru>n||rv>m)//洛谷数据有误...
continue;
build(ru,rv);
}
for(int i=1;i<=n;i++)
{
if(!x[i])
{
memset(vis,0,sizeof(vis));
ans+=path(i);//统计匹配数
}
}
printf("%d",ans);
return 0;
}
二分图的最小顶点覆盖数=二分图的最大匹配数
二分图的最小路径覆盖数=点的数量-二分图的最大匹配数
二分图的最小边覆盖数=点的数量-二分图的最大匹配数
二分图的最大独立集大小=点的数量-二分图的最大匹配数
推荐博客:http://www.cnblogs.com/shenben/p/5573788.html 对匈牙利算法进行详尽的理解
推荐题目:http://codevs.cn/problem/1222/ 信与信封问题
先进行一次匹配,再对匹配的边依次删除,如果不能完美匹配,说明这条边是不可或缺的,则将这条边输出
#include
#include
#include
#include
#include
using namespace std;
int n,x,y,ans;
int lkx[100010],lky[100010],mp[2500][2500];
bool flag;
bool vis[100010];
int path(int k)
{
for(int i=1;i<=n;i++)
{
if(!mp[k][i]&&!vis[i])
{
vis[i]=1;
if(!lky[i]||path(lky[i]))
{
lkx[k]=i;
lky[i]=k;
return 1;
}
}
}
return 0;
}
int main()
{
scanf("%d",&n);
while(1)
{
scanf("%d%d",&x,&y);
if(x==0&&y==0)
break;
mp[x][y]=1;
}
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
if(path(i))
ans++;
}
if(ans!=n)
printf("none");
else
{
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
int tmp=lkx[i];
mp[i][tmp]=1;
lkx[i]=0;
lky[tmp]=0;
if(!path(i))
{
printf("%d %d\n",i,tmp);
lkx[i]=tmp;
lky[tmp]=i;
flag=1;
}
mp[i][tmp]=0;
}
if(!flag)
printf("none");
}
return 0;
}