uval 1099 Work Scheduling(一般图最大匹配)

Description

There is a certain amount of night guards that are available to protect the local junkyard from possible junk robberies. These guards need to be scheduled in pairs so that each pair guards in a 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 a number 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

3
1 2
2 3
1 3

output

2
1 2


题意

一般图匹配模板题,求最大匹配及匹配的一种方案,n个点,边输入到文件结束

思路

本文章供回忆,甚看
英语太渣看不懂论文,加权的是无望了,看这篇博客了解思想,还是太菜无法根据思想敲出代码,照着网上的代码敲了一个链式前向星存图代码,改了点东西。

一般图如果存在带奇数个点的环,那么环中任意一点找到环外的匹配则全部匹配,所以将奇环缩点。
手模一下更易于理解虽然很麻烦。(A点开始


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

const int N = 230;

int first[N], tot = 0;
struct Edge
{
    int v, next;
}e[N*N*2];

void add_edge(int u, int v)
{
    e[tot].v = v;
    e[tot].next = first[u];
    first[u] = tot++;
}

int n, match[N];
int nxt[N], mark[N], vis[N], par[N];
int que[N], index;

int sear(int x) // 并查集
{
    return par[x] == x ? x : par[x] = sear(par[x]);
}

void unit(int a, int b) // 合并
{
    a = sear(a); b = sear(b);
    if(a != b) par[a] = b;
}

int lca(int u, int v) // 找最近公共祖先
{
    static int t = 0;
    ++t;
    while(1)
    {
        if(u != -1)
        {
            u = sear(u);
            if(vis[u] == t) return u;
            vis[u] = t;
            if(match[u] != -1) u = nxt[match[u]];
            else u = -1;
        }
        swap(u,v);
    }
}

void group(int a, int p)
{
    while(a != p)
    {
        // b 不存在为-1的情况,如果存在则交替串长为3, u = p, v 存在匹配, 所以不存在
        int b = match[a], c = nxt[b];

        if(sear(c) != p) nxt[c] = b;
        // 标记为1 的已经找过,查找花内标记为2的值
        if(mark[b] == 2) mark[que[index++] = b] = 1;
        if(mark[c] == 2) mark[que[index++] = c] = 1;

        unit(a,b); // 合并指向p结点
        unit(b,c);
        a = c; // 递归
    }
}

bool augmented(int s)
{
    for(int i = 1; i <= n; ++i) nxt[i] = vis[i] = -1, par[i] = i, mark[i] = 0;
    mark[s] = 1;
    que[0] = s;
    index = 1;

    for(int i = 0; match[s] == -1 && i < index; ++i)
    {
        int u = que[i];
        for(int j = first[u]; ~j; j = e[j].next)
        {
            int v = e[j].v;
            if(match[u] == v) continue; // 已经匹配忽略
            if(sear(u) == sear(v)) continue; // 同一朵花忽略
            if(mark[v] == 2) continue; // T型点忽略
            /*
            if(mark[v] == 1) // 奇环缩点至p
            {
                int p = lca(u,v);
                if(sear(u) != p) nxt[u] = v; // 不在花内
                if(sear(v) != p) nxt[v] = u;
                group(u,p); // 缩点 u-p
                group(v,p);
            }
            */
            if(mark[v] == 1) // 奇环缩点至p
            {
//                与网上的模板不同,本人不保证其正确性,但能ac ural1099
//                感觉加了会快点点,因为花上再套一个花,重叠部分不需要更新,并且u与v的nxt合并时间应该无关紧要
                int p = lca(u,v);
                if(sear(u) != p) nxt[u] = v, group(u,p); // 不在花内
                if(sear(v) != p) nxt[v] = u, group(v,p);
            }
            else if(match[v] == -1) // 增广
            {
                nxt[v] = u;
                for(int y = v; ~y;)
                {
                    int x = nxt[y];
                    int mx = match[x];
                    match[x] = y, match[y] = x;
                    y = mx;
                }
                break;
            }
            else // 交叉链加上v+match[v] 继续寻找
            {
                nxt[v] = u;
                mark[que[index++] = match[v]] = 1;
                mark[v] = 2;
            }
        }
    }
    if(match[s] != -1) return 1;
    return 0;
}

bool g[N][N]; // 怕边太多去重
int main()
{
    scanf("%d",&n);
    int x, y, sum = 0;
    tot = 0;
    memset(first,-1,sizeof(first));
    while(~scanf("%d%d",&x,&y)) if(g[x][y] == 0) add_edge(x,y), add_edge(y,x), g[x][y] = g[y][x] = 1;

    memset(match,-1,sizeof(match));
    for(int i = 1; i <= n; ++i) if(match[i] == -1 && augmented(i)) sum += 2;
    printf("%d\n",sum);
    for(int i = 1; i <= n; ++i) if(match[i] > i) printf("%d %d\n",i,match[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值