zoukankan      html  css  js  c++  java
  • 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,则这条边无用,记录

    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
    ",ans);
            for(int i=0;i<ans;i++){
                printf("%d%c",heap[i],i==ans-1?'
    ':' ');
            }
            if(ans==0)puts("");
        }
        return 0;
    }
    

      

  • 相关阅读:
    组合模式扩展,有选择的递归
    SQL分页查询【转】
    facade外观模式
    C#:几种数据库的大数据批量插入 faib
    装饰模式的扩展
    yeild之我理解
    数据库操作 sqlserver查询存储过程+分页
    SQL Server 索引结构及其使用(二)[转]
    SQL索引使用初步,(转)
    解决多集成,多子类,扩展等 装饰模式
  • 原文地址:https://www.cnblogs.com/xuesu/p/4304623.html
Copyright © 2011-2022 走看看