HDU -1151 有向图最小覆盖路径(模板)

题目传送门
Sample Input

2
4
3
3 4
1 3
2 3
3
3
1 3
1 2
2 3

Sample Output

2
1

题目描述:城镇里的街道从一个交叉口连接到另一个交叉口,街道都是单向的,并且从一个交叉口沿着街道出发不会回到相同的交叉口。伞兵降临在城镇的一个交叉口并可以沿着街道走向另一个没有被其他伞兵走过的交叉口,问城镇中的所有交叉口都被伞兵走过的情况下至少需要多少伞兵。

最小不相交路径覆盖,拆点用二分图求。 最小不相交路径覆盖=点的总数-最大匹配

HK 和 匈牙利算法 都贴一下,以后当模板用。
HK真他妈长,还不如dinic。

//-----匈牙利-------
#include <cstdio>
#include <queue>
const int N=120+5;
const int M=120+5;
const int INF=0x3f3f3f3f;
using namespace std;
typedef long long ll;
int mt[N],head[N];
bool vis[N];
int tot;
struct Edge{int to,nex;}edge[M];
bool dfs(int x)
{
    for(int i=head[x];i;i=edge[i].nex)
    {
        int y=edge[i].to;
        if(!vis[y])
        {
            vis[y]=1;
            if(!mt[y]||dfs(mt[y]))
            {
                mt[y]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int T;
    scanf("%d",&T);
    int n,m;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            head[i]=0,mt[i]=0;
        tot=1;
        int a,b;
        while(m--)
        {
            scanf("%d%d",&a,&b);
            edge[++tot]=(Edge){b,head[a]};head[a]=tot;
        }
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            for(int i=1;i<=n;i++)
                vis[i]=0;
            if(dfs(i))
                ans++;
        }
        printf("%d\n",n-ans);
    }
}

//-------HK--------
#include <cstdio>
#include <queue>
const int N=1000+5;
const int M=12000+5;
const int INF=0x3f3f3f3f;
using namespace std;
typedef long long ll;
struct Edge{int to,nex;}edge[M];
int mt[2*N],d[2*N],head[N];
int tot,n,m,dis;
bool vis[N];
queue<int>q;
bool searchP()
{
    while(!q.empty())
        q.pop();
    for(int i=1;i<=2*n;i++)
        d[i]=0;
    dis=INF;
    for(int i=1;i<=n;i++)
        if(!mt[i])
            q.push(i);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        if(d[x]>dis)
            break;
        for(int i=head[x];i;i=edge[i].nex)
        {
            int y=edge[i].to;
            if(!d[y])
            {
                d[y]=d[x]+1;
                if(!mt[y])
                    dis=d[y];
                else
                {
                    d[mt[y]]=d[y]+1;
                    q.push(mt[y]);
                }
            }
        }
    }
    return dis!=INF;
}
bool dfs(int x)
{
    for(int i=head[x];i;i=edge[i].nex)
    {
        int y=edge[i].to;
        if(!vis[y]&&d[y]==d[x]+1)
        {
            vis[y]=1;
            if(mt[y]&&d[y]==dis)
                continue;
            if(!mt[y]||dfs(mt[y]))
            {
                mt[x]=y;
                mt[y]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        for(int i=1;i<=n;i++)
            head[i]=0;
        tot=1;
        scanf("%d%d",&n,&m);
        int a,b;
        while(m--)
        {
            scanf("%d%d",&a,&b);
            edge[++tot]=(Edge){b+n,head[a]};head[a]=tot;
        }
        int ans=0;
        for(int i=1;i<=2*n;i++)
            mt[i]=0;
        while(searchP())
        {
            for(int i=1;i<=n;i++)
                vis[i+n]=0;
            for(int i=1;i<=n;i++)
                if(!mt[i]&&dfs(i))
                   ans++;
        }
        printf("%d\n",n-ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值