HDU1150(二分图最小点覆盖证明)

题目链接:HDU1150

题目意思:

有A,B两种机器,A机器上有n种模具,B机器上有m种模具,有k个任务,每个任务可以在既可以在A机器上的x模具上生产,也可以在B机器上的y模具上生产。A,B机器都可以切换模具,开始的A,B都为0模具,问最少切换多少次模具。可以完成所有的任务。

题目思路:

如果我们把A机器的n个模具作为二分图的左部,B机器的m个模具作为二分图的右部,然后把每个任务当成一条连接左部x和右部y的边,问题就转化为选择最少的点,覆盖所有的边,边覆盖的定义为该边至少有一个端点在所选点集中。(用最少的模具完成所有任务)。
有这么一个结论:二分图最大匹配 = 二分图最小点覆盖

怎么理解这个结论呢 ?

下面的证明参考这篇博客:大佬的博客
看下面这个二分图,蓝色边是他的匹配边。
在这里插入图片描述
我们可以这么构造一组最小点覆盖的点集。
从二分图的右部的每个未匹配点出发,找一条满足未匹配边-匹配边-未匹配边-匹配边……-未匹配边-匹配边的交叉路,并标记路径上的点。完成之后,左部上标记的点和右部上未标记的点共同组成了一个最小点覆盖点集。(图中红色的点为最小点覆盖点集)
首先这样的点集数量一定等于最大匹配数。
图中蓝色边为匹配边,我们可发现匹配边有的在我们找到的交叉路上,有的不在(图中红色箭头的路径),在路径上的匹配边和左部标记的点一一对应,不在路径上的匹配边和右部上未标记的点一一对应,因为左部标记的点为匹配点,右部未标记的点也是匹配的点,他们都连接一条匹配边,根据这种交叉路的定义(未匹配边-匹配边-未匹配边-匹配边)是不存在一条匹配边左端点标记,右端点未标记,因为一条匹配边左端点标记,右端点必定被标记。所以我们选择点集和匹配边一一对应,等于最大匹配数。
其次为什么这个样的点集一定覆盖所有的边
什么样的边不会被覆盖?左端点未标记,右端点标记的边。
这种情况是不存在的,否则就多出了一条增广路,不符合最大匹配。
为什么是最小的点覆盖?
废话,覆盖所有的匹配边至少需要最大匹配数的点好吗?

代码:

#include<bits/stdc++.h>
using namespace std;
int hungray(const vector<vector<int>>& G, int m) {
    int n = G.size(),ans = 0;
    vector<int>link(n, -1);
    vector<bool>use(n);
    function<bool(int)>dfs=[&](int u) {
        for(auto & i : G[u]) {
            if(!use[i]) {
                use[i] = true;
                if(link[i] == -1 || dfs(link[i])) {
                    link[i] = u;
                    return true;
                }
            }
        }
        return false;
    };
    for(int i = 0; i < m; i++){
        fill(use.begin(),use.end(),false);
        if(dfs(i)) ans++;
    }
    return ans;
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n, m, k;
    while(cin >> n && n) {
        cin >> m >> k;
        vector<vector<int>>G(n + m);
        for(int i = 0; i < k; i++) {
            int id,x, y; cin>>id >> x >> y;
            if(x == 0 || y == 0) continue;
            G[x].push_back(y + n);
        }
        int ans = hungray(G, n);
        cout << ans << endl;
    }
    return 0;
}

自己写了一个代码,求最小点覆盖一组解。不知道有没有错,自己试了几组,没什么太大问题,希望dalao们指错

#include<bits/stdc++.h>
using namespace std;
int flag[100];
int hungray(const vector<vector<int>>G, int m) {
    int n = G.size(), ans = 0;
    vector<int>link(n, -1),use(n, -1);
    memset(flag, 0, sizeof(flag));
    function<int(int, int)>dfs=[&](int u, int d) {
        for(auto& i : G[u]) {
            if(use[i] != d) {
                use[i] = d;
                if(link[i] == -1 || dfs(link[i], d)) {
                    link[i] = u; return 1;
                }
            }
        }
        return 0;
    };
    for(int i = 0; i < m; i++) {
        if(dfs(i,i)) ans++;
    }
    function<void(int ,int)>dfs2=[&](int u, int fa) {
        flag[u] = 1;
        for(auto & i : G[u]) {
            if(i == fa) continue;
            for(auto & j : G[i]) {
                if(link[j] == i) {
                    flag[i] = 1;
                    dfs2(j, i); break;
                }
            }
        }
    };
    for(int i = m; i < n; i++) if(link[i] == -1) dfs2(i, -1);
    return ans;
}
int main() {
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int n, m, k;
    while(cin >> n && n) {
        cin >> m >> k;
        vector<vector<int>>G(n + m);
        for(int i = 0; i < k; i++) {
            int  x, y; cin  >> x >> y;
            G[x].push_back(y);
            G[y].push_back(x);
        }
        cout << hungray(G, n) << endl;
        for(int i  = 0; i < n + m; i++) {
            if(i < n && flag[i]) cout << i << ' ';
            else if(i >= n && flag[i] == 0) cout << i << ' ';
        }
        cout << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值