码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-02-28 06:40:47      阅读:150      评论:0      收藏:0      [点我收藏+]

标签:

http://acm.hdu.edu.cn/showproblem.php?pid=4687

此题求哪些边在任何一般图极大匹配中都无用,对于任意一条边i,设i的两个端点分别为si,ti,

则任意一个极大匹配中都必然有si或ti至少一个点被匹配,当在图中去掉si,ti两个点时,匹配数会损失一个或两个.

如果损失两个,就说明在极大匹配中这两个点分别连接不同的边,于是边i是无用的

所以总体思路:一般图匹配求出最大匹配数cnt0,分别试着去掉每条边的端点,再次匹配,匹配数如果小于cnt0-1,则这条边无用,记录

ATTENTION:如果无用边数为0,仍然需要输出一个空行

带花树思想简介:

对于一对匹配点,设其中一个是S类型点,另外与之配对的是T点,如图

技术分享

那么对于需要被增广的某个S类型点u,以及与它相连的点v有

1. v是T类型点
2. v还没有被匹配过

3. v是S类型点

三种情况

设match[i]是i的匹配点

对于第1)种情况,即使接上u,v,断开v和match[v],重整整个增广路也不会影响结果,忽略

对于第2)种情况,就像二分图一样,直接接上u,v并增广路取反

对于第3)种情况,有a. v不在当前增广路上 b.v在当前增广路上

对于3.a)情况,把v加入当前增广路,把两条增广路合并,或者说是把v的开花树并入u的

对于3.b)情况,设r为v,u的最近公共祖先,那么r-v-u-r形成了奇环,把奇环缩为一点,这个点就是开花算法的花,设新图为G‘,原图为G,可以证明G中有对应G的增广路,因为奇数环上任意一点都可以断开形成新增广路,所以都可以作为s点,不过一次只能断开一处

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <assert.h>
using namespace std;

const int MAXN = 45;
int n;//人数,start from 1
bool del[MAXN];//是否不可用
int e[MAXN][MAXN],sz[MAXN];//图
int match[MAXN];//对应边
bool inQue[MAXN],inPath[MAXN],inBlossom[MAXN];//状态
int que[MAXN],head,tail;//s点加入此队列更新
int start,finish;//增广路取反的开始点,结束点
int newFather;//开花算法
int nxt[MAXN],father[MAXN];//nxt 用于遍历开花树 father 标示所属花

void push(int u){
    que[tail] = u;
    tail++;
    inQue[u] = true;
}
int pop(){
    int res = que[head];
    head++;
    return res;
}
int findCommonAncestor(int u,int v){
    memset(inPath,false,sizeof(inPath));
    while(true){
        u = father[u];
        inPath[u] = true;
        if(u == start) break;
        u = nxt[match[u]];
    }
    while(true){
        v = father[v];
        if(inPath[v])break;
        v = nxt[match[v]];
    }
    return v;
}
void resetTrace(int u){//连环
    int v;
    while(father[u] != newFather){
        v = match[u];
        inBlossom[father[u]] = inBlossom[father[v]] = true;
        u = nxt[v];
        if(father[u] != newFather) nxt[u] = v;
    }
}
void bloosomContract(int u,int v){//连环
    newFather = findCommonAncestor(u,v);
    memset(inBlossom,false,sizeof(inBlossom));
    resetTrace(u);
    resetTrace(v);
    if(father[u] != newFather) nxt[u] = v;
    if(father[v] != newFather) nxt[v] = u;
    for(int tu = 1; tu <= n; tu++)
        if(inBlossom[father[tu]]){
            father[tu] = newFather;
            if(!inQue[tu]) push(tu);
        }
}
void findAugmentingPath(){//增广主过程
    memset(inQue,false,sizeof(inQue));
    memset(nxt,0,sizeof(nxt));
    for(int i = 1;i <= n;i++)father[i] = i;
    head = tail = 1;
    push(start);
    finish = 0;
    while(head < tail){
        int u = pop();
        assert(!del[u]);
        for(int p = 0; p < sz[u]; p++){
            int v=e[u][p];
            if(!del[v] && (father[u] != father[v]) && (match[u] != v)){//v可用,u,v不在同一花中,u,v不是早已连接
                if((v == start) || ((match[v] > 0) && nxt[match[v]] > 0))//奇数环,开花
                    bloosomContract(u,v);
                else if(nxt[v] == 0){//合并开花树
                    nxt[v] = u;
                    if(match[v] > 0)
                        push(match[v]);
                    else{
                        finish = v;//找到配对点,成功
                        return;
                    }
                }
            }
        }
    }
}
void aug(){//增广路取反
    int u,v,w;
    u = finish;
    while(u > 0){
        v = nxt[u];
        w = match[v];
        match[v] = u;
        match[u] = v;
        u = w;
    }
}
void Edmonds(){//增广算法
    memset(match,0,sizeof(match));
    for(int i = 1; i <= n; i++)
        if(!del[i]&&match[i] == 0){
            start = i;
            findAugmentingPath();
            if(finish > 0)aug();
        }
}
int getMatch(){//统计结果
    Edmonds();
    int cnt = 0;
    for(int i = 1; i <= n;i++)
        if(match[i] > 0)
            cnt++;
    return cnt/2;
}

bool g[MAXN][MAXN];
int from[MAXN*4],to[MAXN*4];
int heap[MAXN*4];
int main(){
    int m;
    while(scanf("%d%d",&n,&m)==2){
        memset(g,false,sizeof(g));
        memset(del,false,sizeof(del));
        memset(sz,0,sizeof(sz));
        for(int i = 0;i <m;i++){
            scanf("%d%d",from+i,to+i);
            int f=from[i],t=to[i];
            if(!g[f][t]){
                g[f][t]=g[t][f]=true;
                e[f][sz[f]++]=t;
                e[t][sz[t]++]=f;
            }
        }
        int cnt0 = getMatch();

        int ans=0;
        for(int i = 0;i <m;i++){
            int f=from[i],t=to[i];
            del[f]=del[t]=true;
            int cnt = getMatch();
            if(cnt == cnt0-2){heap[ans++]=i+1;}
            del[t]=del[f]=false;
        }
        printf("%d\n",ans);
        for(int i=0;i<ans;i++){
            printf("%d%c",heap[i],i==ans-1?‘\n‘:‘ ‘);
        }
        if(ans==0)puts("");
    }
    return 0;
}

  

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

标签:

原文地址:http://www.cnblogs.com/xuesu/p/4304623.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!