POJ1486_Sorting Slides_二分图匹配必须边

题意

一些透明的纸杂乱地摞在一起,从上往下依次用大写英文字母编号。每一张纸上都写着一个数字。现在给出纸的边框坐标和数字的坐标,试确定每张纸对应的数字是几。以成对形式输出确定对应的纸编号和数字。如果没有确定的对应,则输出none。

思路

一开始以为是一个简单的二分图模板,然而写出来样例都过不了
这里的“对应”并不是简单的匹配,而是一种确定关系。题意是说确定了某个数字一定在某一张纸上。然后就开始各种打补丁,然而还是没有AC。
看了网上的题解才知道,这种唯一的对应关系称为二分图匹配中的必须边。具体算法过程如下:
首先,根据题意,必定存在完美匹配。所以,先用二分图算法求出一个完美匹配。(这个结果令存一个数组)
然后对于这个完美匹配中的每一条边,在图中删除这条边,然后再进行二分图匹配。如果仍是完美匹配,说明这条边不是必须边,这两个点的关系也不是唯一的对应关系。如果不是完美匹配了,说明这条边是必须边。

题目链接

http://poj.org/problem?id=1486

AC代码

#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>

using namespace std;

const int maxn = 300;

int n;              //纸的数量
int A[maxn][4];     //纸边缘坐标
int B[maxn][2];     //数字坐标
bool G[maxn][maxn]; //纸和数字抽象成点建图
int res[maxn];      //一开始求出的完美匹配
int match[maxn];
bool usd[maxn];

//二分图匹配模板
bool dfs(int v)
{
    usd[v] = true;

    for(int i= n; i< 2*n; i++)
    {
        if(!G[v][i]) continue;

        int u = i, w = match[u];
        if(w < 0 || !usd[w] && dfs(w))
        {
            match[v] = u;
            match[u] = v;
            return true;
        }
    }

    return false;
}

int max_match()
{
    int res = 0;

    memset(match, -1, sizeof match);

    for(int i= 0; i< n; i++)
        if(match[i] < 0)
    {
        memset(usd, 0, sizeof usd);
        if(dfs(i)) res ++;
    }

    return res;
}
//二分图匹配模板

int main()
{
    for(int k= 1; ; k++)
    {
        scanf("%d", &n);
        if(n == 0) break;

        memset(G, false, sizeof G);

        for(int i= 0; i< n; i++)
            for(int j= 0; j< 4; j++)
                cin >> A[i][j];

        for(int i= 0; i< n; i++)
            cin >> B[i][0] >> B[i][1];

        for(int i= 0; i< n; i++)
        {
            for(int j= 0; j< n; j++)
            {
                if(A[i][0] < B[j][0] && A[i][1] > B[j][0]
                    && A[i][2] < B[j][1] && A[i][3] > B[j][1])
                {
                    G[i][n+j] = G[n+j][i] = true;
                }
            }
        }

        max_match();

        //另存完美匹配结果
        for(int i= 0; i< n; i++)
            res[i] = match[i];

        int cnt = 0;//已找出的必须边数

        printf("Heap %d\n", k);

        for(int i= 0; i< n; i++)
        {

            G[i][res[i]] = G[res[i]][i] = false;//在图中删除边

            if(n == max_match()) continue;//仍是完美匹配
            else{                         //不是完美匹配了
                if(cnt ++ > 0) printf(" ");
                printf("(%c,%d)", i+'A', res[i]-n+1);
            }

            G[i][res[i]] = G[res[i]][i] = true;//判定完后恢复边
        }

        if(cnt == 0) printf("none");
        cout << endl << endl;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值