匹配极其相关问题(四)

21 篇文章 0 订阅
7 篇文章 0 订阅

前言:
之前已经介绍了最小点覆盖与最大点独立,那么接下来就应该是最小边覆盖问题了。最小边覆盖问题有很多变种,其中最常见的就是DAG上的最小路径覆盖,这种问题可以转化成二分图最大匹配解决。

基本定理:
根据博客(一),有定理如下:
定理4:二分图中,无孤立点,点独立数=边覆盖数=N-边独立数
若存在M个孤立点,上述定理3、4在顶点数N扣除M后成立。
定理7:无向图中,无孤立点,
1)若M为G的一个最大匹配,对于G中M的每个未覆盖点v,选取一条与v相关联的边所组成的边的集合为N,则W=M∪N为G的最小边覆盖;
2)若W为G的一个最小边覆盖,若W中存在相邻的边就移去一条,设移去的边集为N,则M=W-N为G中一个最大匹配。

算法思路:
最小边覆盖:
根据定理4和7,我们可以得到求解最小边覆盖的算法。最小边覆盖数可以通过求解二分图最大匹配知道,在求得一种匹配方案之后,我们需要找到边覆盖方案,根据定理7,我们知道对于已匹配的点,我们可以直接使用匹配边;对于未匹配点,随便挑选一条相连边。
DAG最小路径覆盖:
这种问题是最小边覆盖的一个变种,解决问题的时候需要将原图的一个点拆成两个点,如果两个结点X和Y存在边,则将左图的X与右图的Y连边。之后求解二分图最大匹配,最终的最小路径覆盖数=结点数-最大匹配数,可以解决。

例题解析:
1、最小边覆盖:
题目链接:URAL 1109
解题思路:
最基本的最小边覆盖,直接使用匈牙利算法求匹配数再计算边覆盖数即可。
代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int n,m,K,pre[1005];
bool a[1005][1005],vis[1005];

bool dfs(int index)
{
    for(int i=1;i<=m;i++)
    {
        if(!vis[i]&&a[index][i])
        {
            vis[i]=1;
            if(!pre[i]||dfs(pre[i]))
            {
                pre[i]=index;
                return 1;
            }
        }
    }
    return 0;
}

int hungary()
{
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));  
        if(dfs(i))
            cnt++;
    }
    return cnt;
}

int main()
{
    while(~scanf("%d %d %d",&n,&m,&K))
    {
        memset(a,0,sizeof(a));  
        memset(pre,0,sizeof(pre));

        int x,y;
        for(int i=0;i<K;i++)
        {
            scanf("%d %d",&x,&y);
            a[x][y]=1;
        }

        printf("%d\n",n+m-hungary());
    }

    return 0;
}

2、DAG最小路径覆盖:
题目链接:UVALIVE 3126
解题思路:
该题目的描述就是一个很裸的DAG最小路径覆盖的问题描述,直接按照前面所说的算法思路可以解决。
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define maxn 505

using namespace std;

int startx[maxn],starty[maxn],endx[maxn],endy[maxn];
int n,vy[maxn],prey[maxn],a[maxn][maxn],t[maxn];

int dist(int x1,int y1,int x2,int y2){
    return abs(x1-x2)+abs(y1-y2);
}

bool dfs(int index)
{
    for(int i=1;i<=n;i++)
    {
        if(!vy[i]&&a[index][i])
        {
            vy[i]=1;
            if(!prey[i]||dfs(prey[i]))
            {
                prey[i]=index;
                return 1;
            }
        }
    }
    return 0;
}

int hungary()
{
    int ans=0;
    memset(prey,0,sizeof(prey));
    for(int i=1;i<=n;i++)
    {
        memset(vy,0,sizeof(vy));
        if(dfs(i))
            ans++;
    }
    return ans;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int h,m;
        for(int i=1;i<=n;i++)
        {
            scanf("%02d:%02d %d %d %d %d",&h,&m,&startx[i],&starty[i],&endx[i],&endy[i]);
            t[i]=h*60+m;
        }

        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++)
        {
            int d=dist(startx[i],starty[i],endx[i],endy[i]);
            for(int j=1;j<=n;j++)
                if(t[i]<t[j] && t[j]-t[i]-1 >= d+dist(endx[i],endy[i], startx[j],starty[j]))
                    a[i][j]=1;
        }
        int ans=hungary();
        printf("%d\n",n-ans);
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值