hiho 第117周 二分图多重匹配,网络流解决

描述

学校的秋季运动会即将开始,为了决定参赛人员,各个班又开始忙碌起来。

小Hi和小Ho作为班上的班干部,统计分配比赛选手的重任也自然交到了他们手上。

已知小Hi和小Ho所在的班级一共有N名学生(包含小Hi和小Ho),编号依次为1..N。

运动会一共有M项不同的比赛,编号为1..M。第i项比赛每个班需要派出m[i]名选手参加。

根据小Hi和小Ho的统计,编号为i的学生表示最多同时参加a[i]项比赛,并且给出他所擅长的b[i]项比赛的编号。

小Hi和小Ho希望将每个学生都安排到他所擅长的比赛项目,以增加夺冠的可能性。同时又要考虑满足每项比赛对人数的要求,当然给一个学生安排的比赛项目也不能超过他愿意参加的比赛项目数量。

根据统计的结果,小Hi和小Ho想知道能否有一个合适的安排,同时满足这些条件。

提示:二分图多重匹配

输入

第1行:1个整数T,表示一共有T(2≤T≤5)组数据,每组数据按如下格式给出:

第1行:2个正整数N,M。1≤N≤100,1≤M≤100。

第2行:M个整数,第i个数表示第i个项目需要的选手数量m[i]。1≤m[i]≤N。

第3..N+2行:若干整数,第i+2行表示编号为i的学生的信息。先是a[i],b[i],接下来b[i]个整数,表示其所擅长的b[i]个项目。1≤a[i]≤M

输出

第1..T行:第i行表示第i组数据能否满足要求,若能够输出"Yes",否则输出"No"。

提示:二分图多重匹配

小Ho:真是麻烦啊,要不让他们自己报名项目怎么样?

小Hi:那样可能满足不了人数的要求啊,还是根据他们的意愿来进行分配吧。

小Ho:要是每个项目都只要派一个人参加就好了。这样只需要一个简单的二分图匹配就可以决定了!

小Hi:但是现在的情况是一个人可以参加多个项目,一个项目也需要多个人参加才行。

小Ho:对啊,所以这就问题所在啊。小Hi你有什么解决的办法么?

小Hi:嗯......有了!

小Ho:什么办法?

小Hi:还是从你刚刚说的二分图匹配入手,我们仍然将学生看作A部,比赛项目看作B部:

接下来,根据每个学生的意愿我们将对应的A[i]和B[j]连起来。比如编号为i的学生擅长编号为j的项目,那么就连接A[i]-B[j]:

小Ho:然后呢?

小Hi:这次的问题和二分匹配不同的地方在于:匹配结果中,A[i]可能和多个B[j]相连,如果有一个可以来记录A[i]连接数量的量呢?

小Ho:记录A[i]的连接数量么?

小Hi:对!因为A[i]最多只能和a[i]的点相连。所以我们需要一个方法来限制这个量在0~a[i]之间变动。

小Ho:这听上去有点像网络流里面的流量,只能在0和容量之间变动。

小Hi:没错!这里我们要用的就是网络流。

小Ho:那具体怎么做呢?

小Hi:我们需要对之前的二分图进行进一步扩展。首先虚拟一个源点s和汇点t:

接下来我们根据a[i],在s和A[i]之间连接一条容量为a[i]的边。

同时将原来A[i]-B[j]直接的边都改造为从A[i]到B[j]的容量为1的边。

最后我们还要连接所有的B[j]-t。由于比赛项目B[j]最多只需要m[j]名选手参加,所以我们不妨限制B[j]-t的容量为m[j]。

这就是我们最后的网络流图。那么小Ho,你想想在这个图上面做的最大流有什么含义?

小Ho:我想想啊......我知道了!

在完成最大流后,这个网络流图的流量情况直接对应了一个分配方案:

s-A[i]:这一类边的流量表示了A[i]参加的项目数量。

A[i]-B[j]:这一类边的流量表示了A[i]是否参加项目B[j],若参加则流量为1,否则流量为0。

B[j]-t:这一类边的流量表示了参加比赛项目B[j]的选手数量。

由于流网络会自动调整去满足最大流量,所以它会自动调整每个A[i]-B[j]的流量来使得B[j]-t尽可能大。

若每个项目都能够满足人数的话,网络流会自己调整为所有B[j]-t都满流的情况。

小Hi:就是这样,我们只需要最后判断一下每一条B[j]-t的边是否满流,就可以知道能否满足需求。同时还可以根据A[i]-B[j]的情况,计算出每个选手所参加的比赛项目。

小Ho:这个方法好厉害,都不需要自己手动去进行调整了。

小Hi:没错,这也就是网络流的神奇之处。对于这类允许多条边的匹配问题,也被称为二分图多重匹配问题。而网络流正是它的一个简单解决方法。

#include <bits/stdc++.h>
using namespace std;

#define maxn 505
#define INF 0x3f3f3f3f

struct Edge
{
    int from,to,cap,flow;
};

struct Dinic
{
    int n,m,s,t;
    vector<Edge> edge;
    vector<int> G[maxn];
    bool vis[maxn];
    int d[maxn];
    int cur[maxn];

    void init()
    {
        for(int i=0;i<maxn;i++)
            G[i].clear();
        edge.clear();
        memset(d,0,sizeof(d));
        memset(vis,0,sizeof(vis));
        memset(cur,0,sizeof(cur));
    }

    void addEdge (int from,int to,int cap)
    {
        edge.push_back((Edge){from,to,cap,0});
        edge.push_back((Edge){to,from,0,0});
        m = edge.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool BFS()
    {
        memset(vis,0,sizeof(vis));
        queue<int> Q;
        Q.push(s);
        d[s] = 0;
        vis[s] = 1;
        while(!Q.empty())
        {
            int x = Q.front();
            Q.pop();
            for(int i=0; i<G[x].size(); i++)
            {
                Edge & e = edge[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow)
                {
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }

    long long DFS(int x,int a)
    {
        if(x==t||a==0) return a;
        long long flow = 0,f;
        for(int & i = cur[x]; i<G[x].size(); i++)
        {
            Edge & e = edge[G[x][i]];
            if(d[x] + 1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)
            {
                e.flow +=f;
                edge[G[x][i]^1].flow -=f;
                flow +=f;
                a-=f;
                if(a==0) break;
            }
        }
        return flow;
    }

    int Maxflow (int s,int t) {
        this->s = s;this->t = t;
        int flow = 0;
        while(BFS()) {
            memset(cur,0,sizeof(cur));
            flow+=DFS(s,INF);
        }
        return flow;
    }

    //求最小割S,T;
    void new_BFS(int s,int n)
    {
        memset(vis,0,sizeof(vis));
        d[s] = 0;
        vis[s] = 1;
        queue<int> Q;
        Q.push(s);
        while(!Q.empty())
        {
            int u = Q.front();
            Q.pop();

            for(int i=0;i<G[u].size();i++)
            {
                Edge & e = edge[G[u][i]];
                if(!vis[e.to]&&e.cap>e.flow)
                {
                    vis[e.to] = 1;
                    d[e.to] = d[u] + 1;
                    Q.push(e.to);
                }
            }
        }

        int cnt = 0;
        for(int i=1;i<=n;i++)
        {
            if(vis[i]) cnt++;
        }
        printf("%d\n",cnt);
        for(int i=1;i<=n;i++)
            if(vis[i]) printf("%d ",i);
        puts("");
    }

}sol;

int main()
{

    int t;
    scanf("%d",&t);
    while(t--)
    {
        sol.init();
        int n,m;
        long long sum = 0;
        scanf("%d%d",&n,&m);
        int s=0,t=n+m+1;
        for(int i=1;i<=m;i++)
        {
            int cap;
            scanf("%d",&cap);
            sum+=cap;
            sol.addEdge(n+i,t,cap);
        }
        for(int i=1;i<=n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            sol.addEdge(0,i,a);
            for(int j=0;j<b;j++)
            {
                int to;
                scanf("%d",&to);
                sol.addEdge(i,n+to,1);  ///容量为1
            }
        }
        //cout<<sol.Maxflow(s,t)<<endl;
        if(sol.Maxflow(s,t)!=sum) puts("No");
        else puts("Yes");
    }
    return 0;
}

之前做过二分图的多重匹配。用的匈牙利,将match加一维成为二维的。加一个cnt数组记录B块匹配了多少。

现在这里变得复杂了,就是每个人可以选多个。

网络流来了,不知道哪个AK大神说的,一切图论皆网络流。

怎么建图:

S到A的容量是ca,B到t的容量是cb,A到B的连线的容量是1。

重点来了,是否B都可以匹配好,就是网络流的最大流是否等于cb的总和。

时间: 2024-11-07 00:46:39

hiho 第117周 二分图多重匹配,网络流解决的相关文章

POJ3189_Steady Cow Assignment(二分图多重匹配/网络流)

解题报告 http://blog.csdn.net/juncoder/article/details/38340447 题目传送门 题意: B个猪圈,N头猪,每头猪对每个猪圈有一个满意值,要求安排这些猪使得最大满意和最小满意的猪差值最小 思路: 二分图的多重匹配问题; 猪圈和源点连边,容量为猪圈容量,猪与汇点连边,容量1; 猪圈和猪之间连线取决所取的满意值范围; 二分查找满意值最小差值的范围. #include <iostream> #include <cstring> #incl

POJ3189_Steady Cow Assignment(二分图多重匹配/网络流+二分构图)

解题报告 http://blog.csdn.net/juncoder/article/details/38340447 题目传送门 题意: B个猪圈,N头猪.每头猪对每一个猪圈有一个惬意值.要求安排这些猪使得最大惬意和最小惬意的猪差值最小 思路: 二分图的多重匹配问题; 猪圈和源点连边,容量为猪圈容量.猪与汇点连边,容量1; 猪圈和猪之间连线取决所取的惬意值范围; 二分查找惬意值最小差值的范围. #include <iostream> #include <cstring> #inc

hihoCoder 1393 网络流三&#183;二分图多重匹配 (网络流学习#3 记录)

题目链接:http://hihocoder.com/problemset/problem/1393 话说我之前一直不知道二分匹配可以用网络流做... #include<cstdio> #include<cstring> #include<queue> using namespace std; const int N=205; struct ss{ int v,c,nxt; } e[N*20]; int head[N],tot,vis[N],n,m,a[N],b[N],s

hihoCoder 1393 网络流三&#183;二分图多重匹配(Dinic求二分图最大多重匹配)

#1393 : 网络流三·二分图多重匹配 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 学校的秋季运动会即将开始,为了决定参赛人员,各个班又开始忙碌起来. 小Hi和小Ho作为班上的班干部,统计分配比赛选手的重任也自然交到了他们手上. 已知小Hi和小Ho所在的班级一共有N名学生(包含小Hi和小Ho),编号依次为1..N. 运动会一共有M项不同的比赛,编号为1..M.第i项比赛每个班需要派出m[i]名选手参加. 根据小Hi和小Ho的统计,编号为i的学生表示最多同时参加

网络流24题 第五题 - PowerOJ1740 CodeVS1905 圆桌问题 二分图多重匹配 网络最大流

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - PowerOJ1740 - 有SPJ - 推荐 题目传送门 - CodeVS1905 - 无SPJ - 0% 通过率(可以用来看题目) 题意概括 有n支队伍,m个组.第i支队伍有a[i]个人,第i个组最多可以有b[i]个人. 现在要求任何两个同队队员不可位于同一组,求是否有方案满足. 输出第一行,表示是否有,如果有,是1,没有的话,输出0: 如果有,接下来n行,第i行a[i]个数,表示第i支队伍的每个人被安

Jamie&#39;s Contact Groups(二分图多重匹配+二分)(网络流)

Jamie's Contact Groups Time Limit:7000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 2289 Description Jamie is a very popular girl and has quite a lot of friends, so she always keeps a very long contact list

poj 3614 Sunscreen 网络流或二分图多重匹配或优先队列

题意: 有C头牛,每头牛有它可以接受的药的最小值和最大值,有L瓶药,每瓶药有一个值u和它最多能给v头牛用,求最多有多少头牛能满足. 分析: 网络流或二分图多重匹配或优先队列,这道题优化了我的dinic模板,原来的模板会TLE... 代码: //poj 3614 //sep9 #include <iostream> #include <queue> #include <algorithm> using namespace std; const int maxN=5005;

Optimal Milking(二分图多重匹配+二分)(网络流)

Optimal Milking Time Limit:2000MS     Memory Limit:30000KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 2112 Description FJ has moved his K (1 <= K <= 30) milking machines out into the cow pastures among the C (1 <= C <= 20

[HihoCoder1393]网络流三&#183;二分图多重匹配

题目大意: 班级有N名学生,运动会有M项不同的比赛,第i项比赛每个班需要派出m_i名选手参加,编号为i的学生最多同时参加给定的b_i项比赛中的任意a_i项比赛.根据统计的结果,想知道能否有一个合适的安排,同时满足这些条件. 思路: 最大流求二分图多重匹配.建立超级源点S.超级汇点T.对于每一个学生i,连一条从S到i的容量为a_i的边.对于每一项比赛i,连一条从i到T的容量为m_i的边.对于每一个学生i和其所擅长的所有比赛j,连一条从i到j的容量为1的边.计算最大流F,当F=Σm_i时,条件满足.