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

  

时间: 2024-12-19 10:03:16

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

HDOJ 4687 Boke and Tsukkomi 一般图最大匹配带花树+暴力

一般图最大匹配带花树+暴力: 先算最大匹配 C1 在枚举每一条边,去掉和这条边两个端点有关的边.....再跑Edmonds得到匹配C2 如果C2+2==C1则这条边再某个最大匹配中 Boke and Tsukkomi Time Limit: 3000/3000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others) Total Submission(s): 649    Accepted Submission(s): 202

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

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

一般图匹配带花树

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

URAL 1099. Work Scheduling 一般图匹配带花树

一般图匹配带花树模版题: 将奇环缩成圈(Blossom),然后找增广路..... 1099. Work Scheduling Time limit: 0.5 second Memory limit: 64 MB There is certain amount of night guards that are available to protect the local junkyard from possible junk robberies. These guards need to sche

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

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

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

ZOJ 3316 Game 一般图最大匹配带花树

一般图最大匹配带花树: 建图后,计算最大匹配数. 如果有一个联通块不是完美匹配,先手就可以走那个没被匹配到的点,后手不论怎么走,都必然走到一个被匹配的点上,先手就可以顺着这个交错路走下去,最后一定是后手没有路可走,因为如果还有路可走,这一条交错路,就是一个增广路,必然有更大的匹配. Game Time Limit: 1 Second      Memory Limit: 32768 KB Fire and Lam are addicted to the game of Go recently.

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

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

一般图最大匹配带花树

参考博客: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)的 简述一下“带花树”算法吧: 它的核心思想还是找增广路.假设已经匹配好了一堆点,