P3386 【模板】二分图匹配(网络流与线性规划24题01飞行员配对方案问题)

3 篇文章 0 订阅
1 篇文章 0 订阅

题目背景

二分图

题目描述

给定一个二分图,结点个数分别为n,m,边数为e,求二分图最大匹配数

输入输出格式

输入格式:
第一行,n,m,e

第二至e+1行,每行两个正整数u,v,表示u,v有一条连边

输出格式:
共一行,二分图最大匹配

输入输出样例

输入样例#1:
1 1 1
1 1
输出样例#1:
1
说明

n,m<=1000,1<=u<=n,1<=v<=m

因为数据有坑,可能会遇到v>m的情况。请把v>m的数据自觉过滤掉。

算法:二分图匹配

网络流与线性规划24题01飞行员配对方案问题

问题描述:
第二次世界大战时期,英国皇家空军从沦陷国征募了大量外籍飞行员。由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2名飞行员,其中1名是英国飞行员,另1名是外籍飞行员。在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英国飞行员很好地配合。如何选择配对飞行的飞行员才能使一次派出最多的飞机。对于给定的外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。
编程任务:
对于给定的外籍飞行员与英国飞行员的配合情况,编程找出一个最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。
数据输入:
由文件input.txt提供输入数据。文件第1行有2个正整数m和n。n是皇家空军的飞行员总数(n<100);m是外籍飞行员数。外籍飞行员编号为1~m;英国飞行员编号为m+1~n。
接下来每行有2个正整数i 和j,表示外籍飞行员i 可以和英国飞行员j 配合。文件最后以2个-1结束。
结果输出:
程序运行结束时,将最佳飞行员配对方案输出到文件output.txt中。第1行是最佳飞行员配对方案一次能派出的最多的飞机数M。接下来M行是最佳飞行员配对方案。每行有2个正整数i 和j,表示在最佳飞行员配对方案中,飞行员i 和飞行员j 配对。 如果所求的最佳飞行员配对方案不存在,则输出‘No Solution!’。
输入文件示例
5 10
1 7
1 8
2 6
2 9
2 10
3 7
3 8
4 7
4 8
5 10
-1 -1
输出文件示例
4

分析:
其实两题都差不多改一下输入输出就好了,下面只给了二分图匹配的代码,用的是dinic算法。
dinic算法:
1、每次以源点为起始点bfs,求出每个点的编号d[i],d[i]表示从源点到i点通过至少几条残余流量大于0的边,能够到达i点。
2、只有那些满足d[u]+1=d[v]的边(u,v)才被视为存在,然后在这里面不断DFS找增广路并增广(其实随便走都可以),如果没有增广路了,那么返回步骤1,如果BFS不到汇点,证明算法结束。

代码:

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

#define N 2000
#define M 40001
#define INF 0x7ffffff

using namespace std;

struct NOTE
{
    int to,c,next;
}e[M];

int head[N],dis[N];
int tot=1,ans;
int n,m,t=0,s;

void add(int x,int y,int c)
{
    e[++tot].to=y;
    e[tot].c=c;
    e[tot].next=head[x];
    head[x]=tot;
}

bool bfs()
{
    memset(dis,-1,sizeof(dis));
    queue<int> Q;
    dis[t]=1;
    Q.push(t);
    int now;
    while (!Q.empty())
    {
        now=Q.front();
        Q.pop() ;
        for (int i=head[now];i;i=e[i].next)
            if (e[i].c && dis[e[i].to]==-1)     
            {
                dis[e[i].to]=dis[now]+1;
                Q.push(e[i].to);
            }
    }
    if (dis[s]!=-1)
        return true;
    else
        return false;
}

int dfs(int x,int maxf) 
{
    if (x==s || maxf==0)
        return maxf;
    int ret=0;
    for (int i=head[x];i;i=e[i].next)
        if (e[i].c && dis[e[i].to]==dis[x]+1)
        {
            int f=dfs(e[i].to,min(maxf-ret,e[i].c));
            e[i].c-=f;
            e[i ^ 1].c+=f;
            ret+=f;
            if (ret==maxf)          
                break;
        }
    return ret; 
}

void dinic()
{
    while (bfs())
    {
        ans+=dfs(t,INF);
    }
}

int main()
{
    int d;
    scanf("%d%d%d" ,&n,&m,&d);
    for (int i=1;i<=n;i++)
    {
        add(t,i,1);
        add(i,t,0);
    }
    int x,y;
    for (int i=1;i<=d;i++)
    {
        scanf("%d%d" ,&x,&y);
        if (y>m || x>n){
            continue;
        }
        add(x,y+n,1);
        add(y+n,x,0);
    }
    s=n+m+1;
    for (int i=1;i<=m;i++)
    {
        add(i+n,s,1);
        add(s,i+n,0);
    }
    ans=0;
    dinic();
    printf("%d\n" ,ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值