先贴上大神博客,再说说自己的理解
http://blog.csdn.net/xuezhongfenfei/article/details/10148445
一般图匹配
嗯
怎么办
我们回想解决二分图匹配的算法
——匈牙利算法
匈牙利算法,
“如果一个男生可以勾搭上一个妹子,
而且使得之前的所有男生都还有妹子,那这个妹子就是他的了!”
怎么办,我们需要带花树算法。
我们先钦定一个匹配两个点哪个是男的哪个是女的,
如果当前一个男点想勾搭一个女点,说明找到一个偶环,无视他(好可怜)
如果一个男点勾搭上一个男点,肿么办?
这说明我们找到一个基环,
你想如果在这个基环上只要有一个人可以在环外找到配偶,
我们就可以一个个确定每个点是攻还是受啊,
是不是很妙。
这个基环就叫花。
这里有一个定理“缩起来的基环和没缩起来是一样的”
那么我们要缩环
在怎么办
是不是很难写?
并不(QWQ雅蠛蝶好多细节)
我们开一个next数组记录增广路径上的后继
并用一个并查集维护一下这个花的根在哪个点
使用BFS,
对于当前处理的一个点x有四种情况,
假设有边(x,y)
1、x和y是cp,忽略
2、y是女的,算啦算啦
3、y是一个单身小gay,嘿嘿嘿,增广一下
4、y是男的,那就把他掰弯,缩掉当前这个基环
引用大神博客的一部分
“
缩点的时候要进行的工作:
1。找x和y的LCA(的根)p。找LCA可以用各种方法。。。直接朴素也行。
2。在Next数组中把x和y接起来(表示它们形成环了!)
3。从x、y分别走到p,修改并查集使得它们都变成一家人,同时沿路把Next数组接起来。
Next数组很奇妙。每时每刻,它实际形成了若干个挂在一起的双向链表来表示一朵花内部的走法。
----
/ \<--+
| | |
| |<--+
v v
----------
/ \
+- --+
| |
| |
+---->s <------+
”
大概就是这样吧
ZOJ 3316
Fire and Lam are addicted to the game of Go recently. Go is one of the oldest board games. It is rich in strategy despite its simple rules. The game is played by two players who alternately place black and white stones on the vacant intersections of a grid of 19*19 lines. Once placed on the board, stones cannot be moved elsewhere, unless they are surrounded and captured by the opponent‘s stones. The object of the game is to control (surround) a larger portion of the board than the opponent.
Fire thinks it is too easy for him to beat Lam. So he thinks out a new game to play on the board. There are some stones on the board, and we don‘t need to care about the stones‘ color in this new game. Fire and Lam take turns to remove one of the stones still on the board. But the Manhattan distance between the removed stone and the opponent‘s last removed stone must not be greater than L. And the one who can‘t remove any stone loses the game.
The Manhattan distance between (xi, yi) and (xj, yj) is |xi - xj| + |yi - yj|.
To show the performance of grace, Fire lets Lam play first. In the beginning of the game, Lam can choose to remove any stone on the board.
Fire and Lam are clever, so they both use the best strategy to play this game. Now, Fire wants to know whether he can make sure to win the game.
Input
There are multiple cases (no more than 30).
In each case, the first line is a positive integer n (n <= 361) which indicates the number of stones left on the board. Following are n lines, each contains a pair of integers x and y (0 <= x, y <= 18), which indicate a stone‘s location. All pairs are distinct. The last line is an integer L (1 <= L <= 36).
There is a blank line between cases.
Ouput
If Fire can win the game, output "YES"; otherwise, just output "NO".
【solution】
两个人在一个棋盘上轮流取石子,你取的石子和上次(不是你的上次)的曼哈顿距离不能超过L,
不能取就输了,问后手能不能赢。
显然如果没有完美匹配后手才有机会赢啊。
#include<stdio.h> #include<stdlib.h> #include<iostream> #include<string> #include<string.h> #include<algorithm> #include<math.h> #include<queue> #include<map> #include<vector> #include<set> #define il inline #define re register using namespace std; const int N=1001; struct edge{int next,to; } e[N*N]; int bel[N],mark[N],match[N],vis[N],next[N],n,m,M,g[N],x[N],y[N],L; queue<int> q; il void addedge(int x,int y){ e[++M]=(edge){g[x],y};g[x]=M; } il void adde(int x,int y){ addedge(x,y);addedge(y,x); } il int getf(int x){ return bel[x]==x?x:bel[x]=getf(bel[x]); } il int unite(int x,int y){ bel[getf(x)]=getf(y); } il int lca(int x,int y){ static int t=0;t++; for(;;){ if(x){ x=getf(x); if(vis[x]==t) return x; vis[x]=t; if(match[x]) x=next[match[x]]; else x=0; } swap(x,y); } } il void flower(int a,int p){ for(;a!=p;){ int b=match[a],c=next[b]; if(getf(c)!=p) next[c]=b; if(mark[b]==2) q.push(b);mark[b]=1; if(mark[c]==2) q.push(c),mark[c]=1; unite(a,b);unite(b,c); a=c; } } il void work(int S){ for(int i=1;i<=n;i++) next[i]=mark[i]=vis[i]=0,bel[i]=i; while(!q.empty()) q.pop(); mark[S]=1;q.push(S); while(!q.empty()){ if(match[S]) return; int x=q.front();q.pop(); for(int i=g[x],y;i;i=e[i].next){ y=e[i].to; if(match[x]==y) continue; if(getf(x)==getf(y)) continue; if(mark[y]==2) continue; if(mark[y]==1){ int r=lca(x,y); if(getf(x)!=r) next[x]=y; if(getf(y)!=r) next[y]=x; flower(x,r);flower(y,r); } else if(!match[y]){ next[y]=x; for(int u=y,v,w;u;){ v=next[u]; w=match[v]; match[v]=u;match[u]=v;u=w; } break; } else{ next[y]=x; mark[match[y]]=1; q.push(match[y]); mark[y]=2; } } } } il void init(){ memset(match,false,sizeof(match)); memset(g,false,sizeof(g));M=0; 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){ adde(i,j); } } for(int i=1;i<=n;i++) if(!match[i]) work(i); for(int i=1;i<=n;i++) if(!match[i]){ cout<<"NO\n"; return; } cout<<"YES\n"; } int main(){ while(scanf("%d",&n)!=EOF) init(); return 0; }
要塞
版权问题,贴一个截图吧
【solution】
这题容易让人头脑发热
“费用流。。。”“最大权闭合。。。”
然并卵,这题是最基础的匹配问题。
什么,是匹配?
我们如图把一条狗拆成七个点
每门大炮依旧是一个点,向能打到的狗的每个蓝点连边。
你会发现,如果蓝点被匹配走了x个,七个点内部匹配的结果就是打狗x点伤害的结果。
妙不妙啊,答案就是ans-n(大炮的数量)
构造出来真的神奇啊,佩服佩服!
#include<stdio.h> #include<stdlib.h> #include<iostream> #include<string> #include<string.h> #include<algorithm> #include<math.h> #include<queue> #include<map> #include<vector> #include<set> #define il inline #define re register #define tag(i,j) ((i-1)*7+j) using namespace std; const int N=1001; struct edge{int next,to; } e[N*N]; int bel[N],mark[N],match[N],vis[N],next[N],n,m,M,g[N],T,ans; queue<int> q; il void addedge(int x,int y){ e[++M]=(edge){g[x],y};g[x]=M; } il void adde(int x,int y){ addedge(x,y);addedge(y,x); } il int getf(int x){ return bel[x]==x?x:bel[x]=getf(bel[x]); } il int unite(int x,int y){ bel[getf(x)]=getf(y); } il int lca(int x,int y){ static int t=0;t++; for(;;){ if(x){ x=getf(x); if(vis[x]==t) return x; vis[x]=t; if(match[x]) x=next[match[x]]; else x=0; } swap(x,y); } } il void flower(int a,int p){ for(;a!=p;){ int b=match[a],c=next[b]; if(getf(c)!=p) next[c]=b; if(mark[b]==2) q.push(b);mark[b]=1; if(mark[c]==2) q.push(c),mark[c]=1; unite(a,b);unite(b,c); a=c; } } il void work(int S){ for(int i=1;i<=n;i++) next[i]=mark[i]=vis[i]=0,bel[i]=i; while(!q.empty()) q.pop(); mark[S]=1;q.push(S); while(!q.empty()){ if(match[S]) return; int x=q.front();q.pop(); for(int i=g[x],y;i;i=e[i].next){ y=e[i].to; if(match[x]==y) continue; if(getf(x)==getf(y)) continue; if(mark[y]==2) continue; if(mark[y]==1){ int r=lca(x,y); if(getf(x)!=r) next[x]=y; if(getf(y)!=r) next[y]=x; flower(x,r);flower(y,r); } else if(!match[y]){ next[y]=x; for(int u=y,v,w;u;){ v=next[u]; w=match[v]; match[v]=u;match[u]=v;u=w; } break; } else{ next[y]=x; mark[match[y]]=1; q.push(match[y]); mark[y]=2; } } } } il void init(){ memset(match,false,sizeof(match)); memset(g,false,sizeof(g));M=ans=0; scanf("%d%d",&n,&m);ans-=n; for(int i=1,x;i<=n;i++){ scanf("%d",&x); for(int j=1,y;j<=x;j++){ scanf("%d",&y); for(int k=1;k<=5;k++) adde(m*7+i,tag(y,k)); } } for(int i=1;i<=m;i++){ for(int j=1;j<5;j++) adde(tag(i,j),tag(i,j+1)); adde(tag(i,5),tag(i,1)); for(int j=1;j<=5;j++){ adde(tag(i,6),tag(i,j)); adde(tag(i,7),tag(i,j)); } } n+=m*7; for(int i=1;i<=n;i++) if(!match[i]) work(i); for(int i=1;i<=n;i++) ans+=(match[i]>i); printf("%d\n",ans); } int main(){ freopen("fortress.in","r",stdin); freopen("fortress.out","w",stdout); scanf("%d",&T); for(int i=1;i<=T;i++){ init(); } return 0; }