• 洛谷 P2756 飞行员配对方案问题


    这题是二分图匹配,题目要求输出最大的配对数,以及配对方案。
    学过网络流之后这题就可以写了,除了外籍飞行员和英国皇家飞行员之外,我们建立一个源点-- 0 和汇点-- n+1。
    让源点连接外籍飞行员,建立一条边流量为1的边,然后建立反边。再给英国皇家飞行员连接上汇点,同样边的流量为1,反边流量为0。
    这样的话网络流的图就建立好了,第一问求的最大匹配数,就是这个图的最大流。对于配对方案的话,一般的写法就是便利所有的外籍飞行员,输出正向边流量为0的就可以了。
    我的写法比较特殊,因为考虑到我们的流可能存在一条边被走了两次,所以,也就是说某个外籍飞行员本身已经匹配过了,但是,另外一个外籍飞行员进行匹配时,走了原本飞行员的配对方案,所以两条路径,一条路径的长度起码包括4个点,另一条路径就是直接的配对方案数。
    如果此时直接输出路径,肯定就输出了三条以上的路径。为了解决这个问题,我们知道,如果有一条边的长度大于两个点,另一条路径的长度等于两个点,并且这两条路都走过同一条边,说明,路径长的那条路,肯定是后走的,短的路肯定是先走的。
    所以我们就从后向前输出,只输出匹配方案数条,并且,如果某个外籍飞行员输出国匹配方案数,我们就不在输出有关他的匹配方案,因为长路径可能交叉。
    对于长路径,如果两个一对输出的话,其实就是匹配方案,画个图观察一下。

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <queue>
    using namespace std;
    
    const int maxn=11000;
    const int INF=1<<30;
    int m,n,ecnt,s,t;
    
    struct Edge {
        int v,w,next;
    };
    
    struct Node {
        int u,ecnt;
    };
    
    Edge edge[2*maxn];
    Node pre[maxn];
    bool vis[maxn];
    int head[maxn];
    bool map[110][110];
    int road[maxn];
    int rcnt=0;
    
    void addEdge(int u,int v,int w)
    {
        edge[ecnt].v=v;
        edge[ecnt].w=w;
        edge[ecnt].next=head[u];
        head[u]=ecnt++;
    }
    
    void init()
    {
        ecnt=0;
        s=0;
        t=n+1;
        memset(edge,0,sizeof(edge));
        memset(head,-1,sizeof(head));
        for (int i=1;i<=m;i++) {
            addEdge(0,i,1);
            addEdge(i,0,0);
        }
        for (int i=m+1;i<=n;i++) {
            addEdge(i,t,1);
            addEdge(t,i,0);
        }
    }
    
    bool bfs()
    {
        memset(vis,0,sizeof(vis));
        memset(pre,-1,sizeof(pre));
        pre[s].u=s;
        vis[s]=true;
        queue<int> q;
        q.push(s);
        while (!q.empty()) {
            int u=q.front();
            q.pop();
            for (int i=head[u];i+1;i=edge[i].next) {
                int v=edge[i].v;
                if (!vis[v]&&edge[i].w) {
                    vis[v]=true;
                    pre[v].u=u;
                    pre[v].ecnt=i;
                    if (v==t) {
                        return true;
                    }
                    q.push(v);
                }
            }
        }
        return false;
    }
    
    int EK()
    {
        int ans=0;
        while (bfs()) {
            int mi=INF;
            for (int i=t;i!=s;i=pre[i].u) {
                mi=min(mi,edge[pre[i].ecnt].w);
                if (i!=t) {
                    road[rcnt++]=i;
                }
            }
            for (int i=t;i!=s;i=pre[i].u) {
                edge[pre[i].ecnt].w-=mi;
                edge[pre[i].ecnt^1].w+=mi;
            }
            ans+=mi;
        }
        return ans;
    }
    
    int main()
    {
        scanf("%d%d",&m,&n);
        init();
        int u,v;
        while (scanf("%d%d",&u,&v)!=EOF) {
            if (u==-1&&v==-1) {
                break;
            }
            addEdge(u,v,1);
            addEdge(v,u,0);
            //map[u][v]=true;
        }
        int ans=EK();
        printf("%d
    ",ans);
        memset(vis,0,sizeof(vis));
        for (int i=rcnt-1;i>0;i-=2) {
            if (ans==0) {
                break;
            }
            if (!vis[road[i]]) {
                ans--;
                vis[road[i]]=true;
                printf("%d %d
    ",road[i],road[i-1]);
            }
        }
        return 0;
    }
    //一个有用的样例
    //5 10
    //3 8
    //3 7
    //1 7
    //-1 -1
    
  • 相关阅读:
    2019-2020-1 20199325《Linux内核原理与分析》第十二周作业
    2019-2020-1 20199325《Linux内核原理与分析》第十一周作业
    2019-2020-1 20199325《Linux内核原理与分析》第九周作业
    2019-2020-1 20199325《Linux内核原理与分析》第八周作业
    20199328 2019-2020-2 《网络攻防实践》第3周作业
    20199328 2019-2020-2 《网络攻防实践》第2周作业
    20199328 2019-2020-2 《网络攻防实践》第1周作业
    20199328《网络攻防实践》假期作业
    2019-2020-1 20199328《Linux内核原理与分析》第十二周作业
    2019-2020-1 20199328《Linux内核原理与分析》第十一周作业
  • 原文地址:https://www.cnblogs.com/xyqxyq/p/12328894.html
Copyright © 2020-2023  润新知