1. 程式人生 > >帶花樹算法

帶花樹算法

stdin 工作 layers spi stat strategy amp inline intersect

先貼上大神博客,再說說自己的理解

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;
}

帶花樹算法