URAL 1099 Work Scheduling(一般图匹配 带花树算法)

URAL - 1099

Time Limit: 500MS Memory Limit: 65536KB 64bit IO Format: %I64d & %I64u

 Status

Description

There is certain amount of night guards that are available to protect the local junkyard from possible junk robberies. These guards need to scheduled in pairs, so that each pair guards at different night. The junkyard CEO ordered you to write a program which given the guards characteristics determines the maximum amount of scheduled guards (the rest will be fired). Please note that each guard can be scheduled with only one of his colleagues and no guard can work alone.

Input

The first line of the input contains one number  N ≤ 222 which is the amount of night guards. Unlimited number of lines consisting of unordered pairs (  i,   j) follow, each such pair means that guard #  i and guard #  j can work together, because it is possible to find uniforms that suit both of them (The junkyard uses different parts of uniforms for different guards i.e. helmets, pants, jackets. It is impossible to put small helmet on a guard with a big head or big shoes on guard with small feet). The input ends with Eof.

Output

You should output one possible optimal assignment. On the first line of the output write the even number  C, the amount of scheduled guards. Then output  C/2 lines, each containing 2 integers (  i,   j) that denote that  i and  j will work together.

Sample Input

input output
3
1 2
2 3
1 3
2
1 2


有n个门卫 给出门卫之间可以两两一起工作的搭配 现在安排门卫工作 

求出可以安排的最多的门卫数 并列举出搭配的方案


与二分匹配相比  一般图并没有二部图的关系  

给出关于带花树算法的介绍  花指的就是图中的环

匹配就是一个图中一堆没有端点的边的集合,求最大匹配就是求这个边集最大有多少条边。

无论是任意图还是二分图,都有以下定理:

当前匹配是最大匹配当且仅当不存在增广路。

增广路的定义就是,一条包含奇数条边的路径,最前和最后的两条边都是非匹配边,且对于路径非两端的点,都连接着一条匹配边和非匹配边。

求图的匹配的算法就是不断地找增广路,把增广路上的匹配边变成非匹配边,非匹配边变成匹配边。

对于二分图来说,只要从一个没有被匹配到的点开始bfs(dfs)一下就能找到增广路(如果确实有增广路)。

但是对于任意图来说,从一个没有被匹配到的点开始bfs(dfs)不一定能找到,能不能找到取决于遍历的顺序。

于是,为了使任意图可以有匹配,带花树就出现。带花树其实就是一棵带着花的树。

任意图中搜索顺序对找增广路有影响主要是因为任意图中有奇数环,在bfs树上出现的这些奇数环就叫做花。

由于这些花不太和谐,所以要进行处理,于是就把这些花缩成一个点,然后继续bfs。

总的来说,带花树的算法就是按照二分图匹配中那样找增广路,遇到花就缩起来。

找到花要把花缩成一点,就要把整个花找出来,这里暴力找一下好了,暴力用的时间等于花上点的个数,由于找完后花就缩起来了,所以找一条增广路中缩点的总时间复杂度是不超过O(V)的。

枚举增广路的初始点是O(V),找一条增广路的时间复杂度是O(E)(邻接矩阵O(V^2))的,所以总的时间复杂度是O(VE)(邻接矩阵(V^3));由此看来其实带花树的时间复杂度和匈牙利算法的时间复杂度是一样的(显然带花树的常数要大很多)。

以上介绍来自: 点击打开链接


这题比较简单 就是根据模板  求出相应的匹配就可以了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#include <vector>
#include <queue>

#define MEM(a,x) memset(a,x,sizeof a)
#define eps 1e-8
#define MOD 10009
#define MAXN 250
#define INF 99999999
#define ll __int64
#define bug cout<<"here"<<endl
#define fread freopen("ceshi.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)

using namespace std;

int Read()
{
    char ch;
    int a = 0;
    while((ch = getchar()) == ' ' | ch == '\n');
    a += ch - '0';
    while((ch = getchar()) != ' ' && ch != '\n')
    {
        a *= 10;
        a += ch - '0';
    }
    return a;
}

void Print(int a)
{
     if(a>9)
         Print(a/10);
     putchar(a%10+'0');
}
deque<int> Q;
//g[i][j]存放关系图:i,j是否有边 match[i]存放i所匹配的点
bool g[MAXN][MAXN],inque[MAXN],inblossom[MAXN],inpath[MAXN];
int match[MAXN],pre[MAXN],base[MAXN];
int n,m,mmg;
//vector<int> res;

struct node
{
    int u,v;
}point[MAXN];
//找公共祖先
int findancestor(int u,int v)
{
    MEM(inpath,0);
    while(1)
    {
        u=base[u];
        inpath[u]=1;
        if(match[u]==-1)  break;
        u=pre[match[u]];
    }
    while(1)
    {
        v=base[v];
        if(inpath[v])  return v;
        v=pre[match[v]];
    }
}

//压缩花
void reset(int u,int anc)
{
    while(u!=anc)
    {
        int v=match[u];
        inblossom[base[u]]=1;
        inblossom[base[v]]=1;
        v=pre[v];
        if(base[v]!=anc)  pre[v]=match[u];
        u=v;
    }
}

void contract(int u,int v,int n)
{
    int anc=findancestor(u,v);
    MEM(inblossom,0);
    reset(u,anc); reset(v,anc);
    if(base[u]!=anc)  pre[u]=v;
    if(base[v]!=anc)  pre[v]=u;
    for(int i=1;i<=n;i++)
    {
        if(inblossom[base[i]])
        {
            base[i]=anc;
            if(!inque[i])
            {
                Q.push_back(i);
                inque[i]=1;
            }
        }
    }
}

bool dfs(int S,int n)
{
    for(int i=0;i<=n;i++)
    {
        pre[i]=-1; inque[i]=0; base[i]=i;
    }
    Q.clear(); Q.push_back(S); inque[S]=1;
    while(!Q.empty())
    {
        int u=Q.front(); Q.pop_front();
        for(int v=1;v<=n;v++)
        {
            if(g[u][v]&&base[v]!=base[u]&&match[u]!=v)
            {
                if(v==S||(match[v]!=-1&&pre[match[v]]!=-1)) contract(u,v,n);
                else if(pre[v]==-1)
                {
                    pre[v]=u;
                    if(match[v]!=-1)
                    {
                        Q.push_back(match[v]);
                        inque[match[v]]=1;
                    }
                    else
                    {
                        u=v;
                        while(u!=-1)
                        {
                            v=pre[u];
                            int w=match[v];
                            match[u]=v;
                            match[v]=u;
                            u=w;
                        }
                        return 1;
                    }
                }
            }
        }
    }
    return 0;
}

int solve()
{
    int ans=0;
    MEM(match,-1);
    for(int i=1;i<=n;i++)
        if(match[i]==-1&&dfs(i,n))  ans++;
    return ans;
}

int main()
{
//    fread;
    scanf("%d",&n);
    int u,v;
    MEM(g,0);
    while(scanf("%d%d",&u,&v)!=EOF)
    {
            g[u][v]=g[v][u]=1;
    }
    int res=solve();
    printf("%d\n",res*2);
    int flag=0;
    for(int i=1;i<=n;i++)
    {
        if(match[i]!=-1)
        {
            printf("%d %d\n",i,match[i]);
            match[i]=match[match[i]]=-1;
        }
    }
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值