今天看了 一般图匹配的带花树算法。
看了几个大佬的博客,也是有点懵:
寻找增广路的做法是:从一个还没被匹配的点(exposed vertex)出发,中间形成交错路径,最后停止在一个没被匹配的点,这就是一条增广路。
定义:在路径上给这些点从1开始标号,奇数的点我们称为外点,偶数的点我们称为内点,可以发现他们恰好对应两个集合(X为外点的集合,Y为内点的集合)
直接寻找增广路做法不适用于一般图匹配的原因:寻找增广路时会形成环,导致有些点既是内点又是外点。找到增广路时都会把未匹配边变成匹配边,匹配边变成未匹配边,若是有些点既是外点又是内点,会匹配出错,即一个点存在于两个匹配中
注意:一般图中只会出现root连两条未匹配边,因为寻找增广路都是从未匹配点开始的。
带花树的做法是:
像匈牙利算法那样不断枚举点寻找增广路,
当找到环时,找到u和v的最近公共祖先,
分别从u和v跑到最近公共祖先的过程中,把环里面的边从有向变成无向的,并环中点的所在集合都设为root
(以上内容大部分为大佬的博客部分内容)
题目:
Ural-1099 Work scheduling 带花树模板题
ZJU 3316 Game 是否存在完美匹配,如果不是先手就可以走未匹配的点,必赢
HDU 3446 daizhenyang's chess 两次匹配,第一次不带king第二次带。若匹配数增加就说明king开始有一条增广路,必赢。
HDU 4687 Boke and Tsukkomi 先求出最大匹配sum,然后枚举每个匹配,删除这个匹配再求最大匹配,若为sum-1就说明这个匹配不是多余的,否则就是多余的
HDU 3551 hard problem 因为要删边使度数减少,所以先拆边,每条边拆成两个点,连边,然后我们把每个点拆成deg[i]-D[i]个点(deg[i]为原图度数,D[i]为子图度数),然后对所有该点和他的边(i,e)组成的点连一条边,表示这个点缺少的度数,可以由这儿得到。接着,求匹配,如果是完美匹配,即:每个度数都找到了可以使他减少的边。这个子图可以得到。
代码还是比较长的。。。
附个人带花树算法模板(Ural-1099 Work scheduling):
#pragma comment(linker,"/STACK:102400000,102400000")
#include
#include
#include
#include
#include
#include
#include
#define maxn 250
#define maxm maxn*maxn*2
#define INF 1e9
using namespace std;
inline int read()
{
register int c=getchar(),fg=1,sum=0;
while(c>'9'||c<'0') {if(c == '-')fg = -1;c=getchar();}
while(c<='9'&&c>='0'){sum=sum*10+c-'0';c=getchar();}
return fg*sum;
}
dequeQ;
bool lk[maxn][maxn];
int n,m,k,p,ans,sum,g[maxm],ql,qr,pre[maxn],base[maxn],tim;
bool inq[maxn],inb[maxn],inp[maxn];
struct node{
int u,v;
}e[maxm];
int lca(int x,int y){
memset(inp,0,sizeof(inp));
while(1){
x=base[x];
inp[x]=1;
if(g[x]==-1) break;
x=pre[g[x]];
}
while(1){
y=base[y];
if(inp[y]) return y;
y=pre[g[y]];
}
}
void sk(int x,int p){
while(x!=p)
{
int y=g[x];
inb[base[x]]=1;
inb[base[y]]=1;
y=pre[y];
if(base[y]!=p) pre[y]=g[x];
x=y;
}
}
void ct(int x,int y,int n)
{
int anc=lca(x,y);
memset(inb,0,sizeof(inb));
sk(x,anc);sk(y,anc);
if(base[x]!=anc) pre[x]=y;
if(base[y]!=anc) pre[y]=x;
for(int i=1;i<=n;i++)
{
if(inb[base[i]]){
base[i]=anc;
if(!inq[i]){
Q.push_back(i);
inq[i]=1;
}
}
}
}
bool dfs(int s,int n){
for(int i=0;i<=n;i++) {pre[i]=-1;inq[i]=0;base[i]=i;}
Q.clear();Q.push_back(s);inq[s]=1;
while(!Q.empty()){
int u=Q.front();Q.pop_front();
for(int v=1;v<=n;v++)
{
if(lk[u][v]&&base[v]!=base[u]&&g[u]!=v)
{
if(v==s||(g[v]!=-1&&pre[g[v]]!=-1)) ct(u,v,n);
else if(pre[v]==-1){
pre[v]=u;
if(g[v]!=-1){
Q.push_back(g[v]);
inq[g[v]]=1;
}
else {
u=v;
while(u!=-1)
{
v=pre[u];
int w=g[v];
g[u]=v;
g[v]=u;
u=w;
}
return 1;
}
}
}
}
}
return 0;
}
int solve()
{
int ans=0;
memset(g,-1,sizeof(g));
for(int i=1;i<=n;i++)
if(g[i]==-1&&dfs(i,n)) ans++;
return ans;
}
int main()
{
int T,cas=1,x,y,c;
scanf("%d",&n);
{
memset(lk,0,sizeof(lk));
int u,v;
while(scanf("%d%d",&u,&v)==2)
{lk[u][v]=lk[v][u]=1;}
int sum=solve();
printf("%d\n",sum*2);
for(int i=1;i<=n;++i){
if(i
另外,贴上F题用到的二分图最大匹配高效率的HK算法个人模板(HDU 2389):复杂度O(sqrt(n)*E)
#pragma comment(linker,"/STACK:102400000,102400000")
#include
#include
#include
#include
#include
#include
#include
#define maxn 3010
#define INF 1e9
using namespace std;
inline int read()
{
register int c=getchar(),fg=1,sum=0;
while(c>'9'||c<'0') {if(c == '-')fg = -1;c=getchar();}
while(c<='9'&&c>='0'){sum=sum*10+c-'0';c=getchar();}
return fg*sum;
}
int n,m,k,p,ans,sum;
vectorG[maxn];
int mx[maxn],my[maxn];
int dx[maxn],dy[maxn];
int dis;
bool used[maxn];
bool findp()
{
queue Q;
dis=INF;
memset(dx,-1,sizeof(dx));
memset(dy,-1,sizeof(dy));
for(int i=0;idis) break;
for(int i=0;i