带花树(一般图最大匹配)详解 ZOJ 3316
2013-09-06 23:59
357 查看
1.增广路:长度为奇数,第一条和最后一条边都是未匹配的边,增光路径是一条匹配边,一条未匹配边这样交错的路径,也叫做交错路
2.二分图已经得到最大匹配当且仅当没有增广路
因为如果这时候还有增广路(长度为2*k+1),其中k条匹配的边,k+1条未匹配的边,其中第一条和最后一条是未匹配的边,我们可以把原来是未匹配的(k+1)变成匹配的,原来匹配的(k)变成未匹配的,结果增加1
所以二分图的匈牙利算法枚举左边的点集,每次若找到增广路,匹配数就+1
该原理是匈牙利算法的核心
3.寻找增广路的做法是:从一个还没被匹配的点(exposed vertex)出发,中间形成交错路径,最后停止在一个没被匹配的点,这就是一条增广路,
定义:在路径上给这些点从1开始标号,奇数的点我们称为外点,偶数的点我们称为内点,可以发现他们恰好对应两个集合(X为外点的集合,Y为内点的集合)
直接寻找增广路做法不适用于一般图匹配的原因:寻找增广路时会形成环,导致有些点既是内点又是外点
那又怎样呢?
http://hi.baidu.com/cloudygoose/item/1ccf42a1c678d0d85af19178
一些题解的说法是二分图中会形成环,但环的边是偶数的,不影响,而在一般图中形成的环是奇数的
这样说是为了解释一般图为什么不合适,实际上在二分图的操作中是不会出现环的,如果出现,说明开始搜索的起点不是一个未匹配的点或者原来的匹配方式有错误(存在一个点存在于两个匹配中)
二分图的特殊性决定了寻找增广路的过程中,外点、内点、外点、内点……这样的分布能对应X集合、Y集合、X集合、Y集合……
但一般图中存在从X集合到X集合的边和从Y集合到Y集合的边,若直接寻找增广路会有些点既是内点又是外点,如下图,v既是内点又是外点,所以才会有缩点等一系列操作,都是为了缩点后能继续用增广路的做法
你会问,既是外点又是内点又怎样呢?
找到增广路时都会把未匹配边变成匹配边,匹配边变成未匹配边,若是有些点既是外点又是内点,会匹配出错,即一个点存在于两个匹配中
****值得注意的是,一般图中若出现环只可能是如下图的情形,即root连2条未匹配边,因为寻找增广路是从一个未匹配点出发的
4.带花树的做法是:
像匈牙利算法那样不断枚举点寻找增广路,
当找到环时,找到u和v的最近公共祖先,
分别从u和v跑到最近公共祖先的过程中,把环里面的边从有向变成无向的,并环中点的所在集合都设为root
http://twinsclover.is-programmer.com/posts/21598.html
ZOJ 3316
图中每个连通块中都存在完美匹配时,后手才会赢
2.二分图已经得到最大匹配当且仅当没有增广路
因为如果这时候还有增广路(长度为2*k+1),其中k条匹配的边,k+1条未匹配的边,其中第一条和最后一条是未匹配的边,我们可以把原来是未匹配的(k+1)变成匹配的,原来匹配的(k)变成未匹配的,结果增加1
所以二分图的匈牙利算法枚举左边的点集,每次若找到增广路,匹配数就+1
该原理是匈牙利算法的核心
3.寻找增广路的做法是:从一个还没被匹配的点(exposed vertex)出发,中间形成交错路径,最后停止在一个没被匹配的点,这就是一条增广路,
定义:在路径上给这些点从1开始标号,奇数的点我们称为外点,偶数的点我们称为内点,可以发现他们恰好对应两个集合(X为外点的集合,Y为内点的集合)
直接寻找增广路做法不适用于一般图匹配的原因:寻找增广路时会形成环,导致有些点既是内点又是外点
那又怎样呢?
http://hi.baidu.com/cloudygoose/item/1ccf42a1c678d0d85af19178
一些题解的说法是二分图中会形成环,但环的边是偶数的,不影响,而在一般图中形成的环是奇数的
这样说是为了解释一般图为什么不合适,实际上在二分图的操作中是不会出现环的,如果出现,说明开始搜索的起点不是一个未匹配的点或者原来的匹配方式有错误(存在一个点存在于两个匹配中)
二分图的特殊性决定了寻找增广路的过程中,外点、内点、外点、内点……这样的分布能对应X集合、Y集合、X集合、Y集合……
但一般图中存在从X集合到X集合的边和从Y集合到Y集合的边,若直接寻找增广路会有些点既是内点又是外点,如下图,v既是内点又是外点,所以才会有缩点等一系列操作,都是为了缩点后能继续用增广路的做法
你会问,既是外点又是内点又怎样呢?
找到增广路时都会把未匹配边变成匹配边,匹配边变成未匹配边,若是有些点既是外点又是内点,会匹配出错,即一个点存在于两个匹配中
****值得注意的是,一般图中若出现环只可能是如下图的情形,即root连2条未匹配边,因为寻找增广路是从一个未匹配点出发的
4.带花树的做法是:
像匈牙利算法那样不断枚举点寻找增广路,
当找到环时,找到u和v的最近公共祖先,
分别从u和v跑到最近公共祖先的过程中,把环里面的边从有向变成无向的,并环中点的所在集合都设为root
http://twinsclover.is-programmer.com/posts/21598.html
ZOJ 3316
图中每个连通块中都存在完美匹配时,后手才会赢
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define N 402 int base ,pre ,match ,que ,inque ,inpath ,inblossom ,front,rear; int n,x ,y ,L,g ; int findancestor(int u,int v) { memset(inpath,0,sizeof(inpath)); while(1) { u=base[u]; inpath[u]=1; if(match[u]==-1) break; u=pre[match[u]]; } while(1) { v=base[v]; if(inpath[v]) return v; v=pre[match[v]]; } } void reset_trace(int u,int anc) { while(u!=anc) { int v=match[u]; inblossom[base[u]]=1; inblossom[base[v]]=1; v=pre[v]; if(base[v]!=anc) pre[v]=match[u]; u=v; } } void contract(int u,int v) { int anc=findancestor(u,v); memset(inblossom,0,sizeof(inblossom)); reset_trace(u,anc); reset_trace(v,anc); if(base[u]!=anc) pre[u]=v; if(base[v]!=anc) pre[v]=u; for(int i=1;i<=n;++i) if(inblossom[base[i]]) { base[i]=anc; if(!inque[i]) { inque[i]=1; que[rear++]=i; } } } bool bfs(int st) { front=rear=0; for(int i=1;i<=n;++i) pre[i]=-1,inque[i]=0,base[i]=i; que[rear++]=st;inque[st]=1; while(front<rear) { int u=que[front++]; for(int v=1;v<=n;++v) if(g[u][v]&&base[u]!=base[v]&&match[u]!=v) { if(v==st||(match[v]!=-1&&pre[match[v]]!=-1)) // circle contract(u,v); else if(pre[v]==-1) { pre[v]=u; if(match[v]!=-1) que[rear++]=match[v],inque[match[v]]=1; else { u=v; while(u!=-1) { v=pre[u]; int w=match[v]; match[u]=v;match[v]=u; u=w; } return true; } } } } return false; } int num ,sz,vis ,pp ; void dfs(int u) { vis[u]=sz;num[sz]++; for(int i=1;i<=n;++i) if(g[u][i]&&!vis[i]) dfs(i); } bool solve() { for(int i=1;i<=n;++i) if(match[i]==-1) bfs(i); for(int i=1;i<=n;++i) if(match[i]!=-1) pp[vis[i]]++; for(int i=1;i<=sz;++i) if(pp[i]!=num[i]) return false; return true; } void init() { memset(g,0,sizeof(g)); memset(match,-1,sizeof(match)); } int abs(int a) {return a>0?a:-a;} int main () { while(scanf("%d",&n)!=EOF) { init(); for(int i=1;i<=n;++i) scanf("%d%d",&x[i],&y[i]); scanf("%d",&L); for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) if(abs(x[i]-x[j])+abs(y[i]-y[j])<=L) { g[i][j]=g[j][i]=1; } memset(num,0,sizeof(num)); memset(vis,0,sizeof(vis)); memset(pp,0,sizeof(pp)); sz=0; for(int i=1;i<=n;++i) if(!vis[i]) { sz++; dfs(i); } if(solve()) printf("YES\n"); else printf("NO\n"); } return 0; }
相关文章推荐
- ZOJ 3316 Game 一般图最大匹配带花树
- ZOJ 3316 Game 一般图最大匹配带花树
- zoj 3316 Game 一般图最大匹配+博弈 有N个棋子在棋盘上,2个人轮流拿走一个棋子,第一步可以拿任意一个,而之后每一步必须拿上一步拿走的棋子曼哈顿长度L以内的棋子,问,后手是否能赢
- zoj 3316 Game (一般图匹配带花树)
- 带花树模板(一般图最大匹配)
- 一般图最大匹配--带花树
- UOJ-79 一般图的最大匹配(带花树模板求解)
- 【BZOJ4405】挑战NPC 带花树模板 一般图最大匹配
- zoj3316【一般图最大匹配 带花树开花】
- HDOJ 4687 Boke and Tsukkomi 一般图最大匹配带花树+暴力
- 【转】带花树(一般无向图的最大匹配)
- HDU 3551 Hard Problem 一般图的最大匹配(带花树)
- Work Scheduling URAL - 1099 一般图的最大匹配(带花树)
- 【Learning】带花树——一般图最大匹配
- 一般图最大匹配问题-带花树开花算法
- 一般图最大匹配 带花树算法 模板
- 带花树 一般图最大匹配
- 一般图最大匹配问题-带花树开花算法
- URAL1099-Work Scheduling(一般图最大匹配(带花树))
- 利用带花树算法解决一般图的最大匹配