带花树算法

先贴上大神博客,再说说自己的理解

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 <= xy <= 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;
}
时间: 2024-10-16 12:57:10

带花树算法的相关文章

带花树算法学习笔记

带花树算法学习笔记 难得yyb写了一个这么正式的标题 Q:为啥要学带花树这种东西啊? A:因为我太菜了,要多学点东西才能不被吊打 Q:为啥要学带花树这种东西啊? A:因为我做自己的专题做不动了,只能先去"预习"ppl的专题了 Q:为啥要学带花树这种东西啊? A:因为可以用来做题啊,比如某WC题目 先推荐一个很皮很皮的带花树讲解: 戳这里嗷 QaQ 言归正传 带花树的算法用来解决一般图的最大匹配问题 说起来,是不是想起来网络流里面的最小路径覆盖? 或者二分图的最大匹配的问题? 的确,带花

URAL 1099 Work scheduling 一般图的最大匹配 带花树算法(模板)

R - Work scheduling Time Limit:500MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice URAL 1099 Description There is certain amount of night guards that are available to protect the local junkyard from possible junk r

kuangbin带你飞 匹配问题 二分匹配 + 二分图多重匹配 + 二分图最大权匹配 + 一般图匹配带花树

二分匹配:二分图的一些性质 二分图又称作二部图,是图论中的一种特殊模型. 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图. 1.一个二分图中的最大匹配数等于这个图中的最小点覆盖数 König定理是一个二分图中很重要的定理,它的意思是,一个二分图中的最大匹配数等于这个图中的最小点覆盖数.如果你还不知道什么是最小点覆盖,我也在这里说一下:假如选

HDU 4687 Boke and Tsukkomi(一般图匹配|带花树)

比赛的时候刚开始看这题还以为是二分图匹配,后来才发现根本不是,因为该题存在长度为奇数的圈 .  比如1->2,2->3,3->1 . 所以该题要用一般图匹配,即带花树算法 . 比赛时抄的模板有地方抄错了,上述样例出现了死循环 .   赛后补题的时候用map去重却得不到正确答案,不知为何,暂放 ,下面给出一种正确解法. 细节参见代码: #include<cstdio> #include<cstring> #include<iostream> #inclu

带花树学习

被大神hzm鄙视了一番,我便觉得这个带花树非学不可啦!!话不多说,下面就是我的学习随笔! 带花树算法就是用来解决一般图的匹配问题.一般图匹配自然是比二分图匹配高级的东西!所以立马屁颠屁颠地去复习了匈牙利算法.这两个算法的核心思想都是"增广"!既然这样,我们就通过对匈牙利算法增广概念的复习来引入带花树算法的增广从而理解带花树算法吧!!_(:зゝ∠)_ 匈牙利算法的增广路 咳咳,我们来复习一下二分图匹配的问题吧!已知集合S和集合T以及若干S集合中的元素到T集合中的元素的二元关系(s,t).

一般图最大匹配带花树

参考博客:http://blog.sina.com.cn/s/blog_95ec9e7401018bga.html https://www.cnblogs.com/owenyu/p/6858508.html 用Dinic实现的二分图匹配的时间复杂度其实是O(M*N^0.5),这也许能够解释为什么一般网络流算法比Hungry要快了. 另外,带花树算法的正确性的证明比较困难:而其时间复杂度是可以做到O(M*N^0.5)的 简述一下“带花树”算法吧: 它的核心思想还是找增广路.假设已经匹配好了一堆点,

poj 3020 一般图最大匹配 带花树开花算法

题意: 给出一个h*w的图,每个点都是'o'或'*',最少要用多少个1*2的矩形才能把图中所有的'*'都覆盖掉. 限制: 1 <= h <= 40; 1 <= w <= 10 思路: 最小边覆盖=|V|-最大匹配 一般图最大匹配,带花树开花算法 /*poj 3020 一般图最大匹配 带花树开花算法 题意: 给出一个h*w的图,每个点都是'o'或'*',最少要用多少个1*2的矩形才能把图中所有的'*'都覆盖掉. 限制: 1 <= h <= 40; 1 <= w &l

HDU 4687 Boke and Tsukkomi 一般图匹配,带花树,思路,输出注意空行 难度:4

http://acm.hdu.edu.cn/showproblem.php?pid=4687 此题求哪些边在任何一般图极大匹配中都无用,对于任意一条边i,设i的两个端点分别为si,ti, 则任意一个极大匹配中都必然有si或ti至少一个点被匹配,当在图中去掉si,ti两个点时,匹配数会损失一个或两个. 如果损失两个,就说明在极大匹配中这两个点分别连接不同的边,于是边i是无用的 所以总体思路:一般图匹配求出最大匹配数cnt0,分别试着去掉每条边的端点,再次匹配,匹配数如果小于cnt0-1,则这条边无

任意图匹配 带花树模版(转)

匹配就是一个图中一堆没有端点的边的集合,求最大匹配就是求这个边集最大有多少条边. 无论是任意图还是二分图,都有以下定理: 当前匹配是最大匹配当且仅当不存在增广路. 增广路的定义就是,一条包含奇数条边的路径,最前和最后的两条边都是非匹配边,且对于路径非两端的点,都连接着一条匹配边和非匹配边. 求图的匹配的算法就是不断地找增广路,把增广路上的匹配边变成非匹配边,非匹配边变成匹配边. 对于二分图来说,只要从一个没有被匹配到的点开始bfs(dfs)一下就能找到增广路(如果确实有增广路). 但是对于任意图