也就是在无向图中,两个结点之间有边表示这两个点可以匹配,然后要求出最大匹配。该算法为带花树,我现在并不理解它,只是当个板子记下来。
const int maxn=1005;
const int maxm=1e6+5;
struct daihuashu
{
struct edge {int v,nxt;} e[maxm];
int n,m,que[maxm],ql,qr,pre[maxn],tim=0,ans=0;
int h[maxn],tot=0,match[maxn],f[maxn],tp[maxn],tic[maxn];
daihuashu() {}
daihuashu(int n,int m) {this->n=n; this->m=m;}
void init(int n=0,int m=0)
{
tim=ans=ql=qr=tot=0;
this->n=n; this->m=m;
mem(match,0); mem(tic,0); mem(h,0);
}
int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
void add(int u,int v) {e[++tot]=(edge){v,h[u]}; h[u]=tot;}
void addb(int u,int v) {add(u,v); add(v,u);}
int lca(int x,int y)
{
for(++tim;;swap(x,y)) if(x)
{
x=find(x);
if(tic[x]==tim) return x;
else tic[x]=tim,x=pre[match[x]];
}
}
void shrink(int x,int y,int p)
{
while(find(x)!=p)
{
pre[x]=y; y=match[x];
if(tp[y]==2) tp[y]=1,que[++qr]=y;
if(find(x)==x) f[x]=p;
if(find(y)==y) f[y]=p;
x=pre[y];
}
}
bool aug(int s)
{
REP(i,1,n) f[i]=i;
mem(tp,0); mem(pre,0); tp[que[ql=qr=1]=s]=1;
while(ql<=qr)
{
int x=que[ql++];
for(int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v)
{
if(find(v)==find(x) || tp[v]==2) continue;
if(!tp[v])
{
tp[v]=2; pre[v]=x;
if(!match[v])
{
for(int now=v,last,tmp;now;now=last)
last=match[tmp=pre[now]],match[now]=tmp,match[tmp]=now;
return true;
}
tp[match[v]]=1; que[++qr]=match[v];
}
else if(tp[v]==1)
{
int l=lca(x,v);
shrink(x,v,l); shrink(v,x,l);
}
}
}
return false;
}
void run() {REP(i,1,n) ans+=(!match[i] && aug(i));}
}dhs;
其中 ans 表示最大匹配的数目,match 数组记录了每个结点与之匹配的结点编号(如果没有则为 0)。复杂度为 O ( n 3 ) O(n^3) O(n3) 。
- 一道例题 Hard Problem :有一个无向图,现在为每个结点指定一个非负整数 d[i],问能否从无向图中构造一个子图,使得每个结点的度为 d[i] 。
做法:对于每个结点进行度拆点,比如一个结点的度为 3,那么就拆成 3 个结点;然后对于每一条边进行边拆点,比如有一条边 <1, 2>,那么拆成两个点;边拆点之后的两个点连边,边拆点的两个端点分别与对应的度拆点的所有点连边;然后求一般图最大匹配,如果每个(拆点后的)结点都可以匹配,那么就存在方案。
这是因为,如果每个结点都可以匹配,如果匹配的两个结点都是边拆点,那么表示舍弃这条边,如果匹配的两个结点一个是度拆点一个是边拆点,那么表示满足了一个度。或者可以这么考虑,每一条边拆点之后,要么两个点相互匹配(表示舍弃这条边),要么这两个点分别和另外两个点匹配(表示满足两个度)。
代码:
#define DIN freopen("input.txt","r",stdin);
#define DOUT freopen("output.txt","w",stdout);
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> P;
int read()
{
int x=0,flag=1; char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=1005;
const int maxm=1e6+5;
struct daihuashu
{
struct edge {int v,nxt;} e[maxm];
int n,m,que[maxm],ql,qr,pre[maxn],tim=0,ans=0;
int h[maxn],tot=0,match[maxn],f[maxn],tp[maxn],tic[maxn];
daihuashu() {}
daihuashu(int n,int m) {this->n=n; this->m=m;}
void init(int n=0,int m=0)
{
tim=ans=ql=qr=tot=0;
this->n=n; this->m=m;
mem(match,0); mem(tic,0); mem(h,0);
}
int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
void add(int u,int v) {e[++tot]=(edge){v,h[u]}; h[u]=tot;}
void addb(int u,int v) {add(u,v); add(v,u);}
int lca(int x,int y)
{
for(++tim;;swap(x,y)) if(x)
{
x=find(x);
if(tic[x]==tim) return x;
else tic[x]=tim,x=pre[match[x]];
}
}
void shrink(int x,int y,int p)
{
while(find(x)!=p)
{
pre[x]=y; y=match[x];
if(tp[y]==2) tp[y]=1,que[++qr]=y;
if(find(x)==x) f[x]=p;
if(find(y)==y) f[y]=p;
x=pre[y];
}
}
bool aug(int s)
{
REP(i,1,n) f[i]=i;
mem(tp,0); mem(pre,0); tp[que[ql=qr=1]=s]=1;
while(ql<=qr)
{
int x=que[ql++];
for(int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v)
{
if(find(v)==find(x) || tp[v]==2) continue;
if(!tp[v])
{
tp[v]=2; pre[v]=x;
if(!match[v])
{
for(int now=v,last,tmp;now;now=last)
last=match[tmp=pre[now]],match[now]=tmp,match[tmp]=now;
return true;
}
tp[match[v]]=1; que[++qr]=match[v];
}
else if(tp[v]==1)
{
int l=lca(x,v);
shrink(x,v,l); shrink(v,x,l);
}
}
}
return false;
}
void run() {REP(i,1,n) ans+=(!match[i] && aug(i));}
int from[maxn],to[maxn],du[maxn];
void solve()
{
n=read(),m=read();
int cnt=0;
VI di[maxn];
REP(i,1,m)
{
int x=read(),y=read();
from[i]=x; to[i]=y;
}
REP(i,1,n) du[i]=read();
REP(i,1,n) REP(j,1,du[i]) di[i].pb(++cnt);
REP(i,1,m)
{
int x=from[i],y=to[i];
for(int j:di[x]) addb(j,cnt+1);
for(int j:di[y]) addb(j,cnt+2);
addb(cnt+1,cnt+2);
cnt+=2;
}
n=cnt;
run();
int flag=1;
REP(i,1,n) if(!match[i]) flag=0;
puts(flag?"YES":"NO");
}
}dhs;
int main()
{
int T=read();
REP(i,1,T)
{
printf("Case %d: ",i);
dhs.init();
dhs.solve();
}
return 0;
}