刚学习了一般图匹配的带花树算法。
算法具体过程的讲解请参见fhq大牛博客:
http://fanhq666.blog.163.com/blog/static/8194342620120304463580/
来看这个题zoj3316 Game。
解法是:如果存在完美匹配,则后手赢。否则先手赢。
证明比较简单:
1)不存在完美匹配时,先手胜:
首先把图按照距离小于L的连边,则图可以分为几个连通块。
由于先手可以选择任何位置,如果其中一个连通块先手胜利,则先手胜利。
那么就假设所有点都在一个连通块中。
如果不存在完美匹配,求出的最大匹配是a1&b1,a2&b2...未匹配点是c1,c2..
那么先手取c1,后手只能拆散取a、b中的某个点,这样就拆散了一个匹配。
假设后手取的是a1,那么先手取b1,后手又要拆散某个匹配。
后手不可能再取c集合中的点,否则最大匹配将大于所求匹配数。
这样,后手总有一次取不到点,所以先手胜。
2)如果存在完美匹配,则后手胜:
后手总取先手取的点的匹配点即可。
算法具体过程的讲解请参见fhq大牛博客:
http://fanhq666.blog.163.com/blog/static/8194342620120304463580/
来看这个题zoj3316 Game。
解法是:如果存在完美匹配,则后手赢。否则先手赢。
证明比较简单:
1)不存在完美匹配时,先手胜:
首先把图按照距离小于L的连边,则图可以分为几个连通块。
由于先手可以选择任何位置,如果其中一个连通块先手胜利,则先手胜利。
那么就假设所有点都在一个连通块中。
如果不存在完美匹配,求出的最大匹配是a1&b1,a2&b2...未匹配点是c1,c2..
那么先手取c1,后手只能拆散取a、b中的某个点,这样就拆散了一个匹配。
假设后手取的是a1,那么先手取b1,后手又要拆散某个匹配。
后手不可能再取c集合中的点,否则最大匹配将大于所求匹配数。
这样,后手总有一次取不到点,所以先手胜。
2)如果存在完美匹配,则后手胜:
后手总取先手取的点的匹配点即可。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define Maxn 370
int set[Maxn];
int vis[Maxn],next[Maxn],mark[Maxn],spouse[Maxn];
int q[Maxn*2],head,tail;
int n;
int tlev=0;
int fi[Maxn],ne[Maxn*Maxn],to[Maxn*Maxn],te;
int g[Maxn][Maxn];
void addedge(int u,int v){te++;ne[te]=fi[u];fi[u]=te;to[te]=v;}
int findset(int x){return x==set[x]?x:set[x]=findset(set[x]);}
void mergeset(int x,int y){x=findset(x);y=findset(y);if (x!=y) set[x]=y;}
int findlca(int u,int v){
tlev++;
while(1){
if (u!=-1){
u=findset(u);
if (vis[u]==tlev) return u;
vis[u]=tlev;
if (spouse[u]!=-1) u=next[spouse[u]];
else u=-1;
}
swap(u,v);
}
}
void group(int a,int p){
int b,c;
while(a!=p){
b=spouse[a];c=next[b];
if (findset(c)!=p) next[c]=b;
if (mark[b]==2) mark[q[tail++]=b]=1;
if (mark[c]==2) mark[q[tail++]=c]=1;
mergeset(a,b);
mergeset(b,c);
a=c;
}
}
void findaugment(int s){
int i,j,k,l,e,u,v,p;
for(i=1;i<=n;++i){
next[i]=-1;set[i]=i;mark[i]=0;vis[i]=-1;
}
q[head=0]=s;tail=1;mark[s]=1;
while(head<tail){
if (spouse[s]!=-1) break;
u=q[head++];
for(e=fi[u];~e;e=ne[e]){
v=to[e];
if (spouse[u]!=v&&findset(u)!=findset(v)&&mark[v]!=2){
if (mark[v]==1){
p=findlca(u,v);
if (findset(u)!=p) next[u]=v;
if (findset(v)!=p) next[v]=u;
group(u,p);
group(v,p);
}
else if (spouse[v]==-1){
next[v]=u;
for(j=v;j!=-1;){
k=next[j];
l=spouse[k];
spouse[k]=j;spouse[j]=k;
j=l;
}
break;
}
else{ //spouse[v]!=-1
next[v]=u;
mark[q[tail++]=spouse[v]]=1;
mark[v]=2;
}
}
}
}
}
int work(){
int i,ret;
for(i=1;i<=n;++i) spouse[i]=-1;
for(i=1;i<=n;++i)if (spouse[i]==-1) findaugment(i);
ret=0;
for(i=1;i<=n;++i)
if (~spouse[i]) ret++;
return ret;
}
int x[Maxn],y[Maxn];
int abs(int x){return x<0?-x:x;}
int main(){
int i,ans,j,l;
while(scanf("%d",&n)!=EOF){
for(i=1;i<=n;++i){
scanf("%d%d",&x[i],&y[i]);
}
scanf("%d",&l);
te=0;
memset(fi,-1,sizeof(fi));
for(i=1;i<=n;++i)
for(j=i+1;j<=n;++j)
if (abs(x[i]-x[j])+abs(y[i]-y[j])<=l){
addedge(i,j);
addedge(j,i);
}
ans=work();
if (ans==n) printf("YES\n");
else printf("NO\n");
}
return 0;
}