hiho一下 第117周 网络流三·二分图多重匹配

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB
描述

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

小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"。

样例输入
2
4 3
1 2 2
1 2 1 2
2 2 1 3
1 1 2
1 2 2 3
4 3
2 2 2
1 2 1 2
2 2 1 3
1 1 2
1 2 2 3
样例输出
Yes
No


多对多的一个类似二分图的题。。居然也就叫二分图多重匹配了。。。

题意中,一个学生只能去其擅长的项目,不能去其他项目,这就给了很大的限定了。


好吧其实是个网络流的题。。具体思路hiho上讲了。。

主要构图的条件转化


学生与项目之间连容量为1的边

项目到汇点的容量为 所需学生数

学生到源点的容量为学生所能接受最大项目场数


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <set>
#include <vector>
#include<stack>
using namespace std;
#include <fstream>
const int N=250;
const int inf=2147483647;
int max_flow;//max_flow是最大流
int mp[N][N];// mp[i][j]是每条边的容量,flow[i][j]是每条边的流量
int pre[N];//res[]是每个点的剩余流量,pre[]是每个点的父亲
int n,f,d;
int level[N];//记录距离标号
int gap[N];//gap常数优化

//入口参数vs源点,vt汇点
int SAP(int vs,int vt)
{
    memset(pre,-1,sizeof(pre));
    memset(level,0,sizeof(level));
    memset(gap,0,sizeof(gap));
    gap[0]=vt;
    int v,u=pre[vs]=vs,maxflow=0,aug=inf;
    while(level[vs]<vt)
    {
        //寻找可行弧
        for(v=1; v<=vt; v++)
        {
            if(mp[u][v]>0&&level[u]==level[v]+1)
            {
                break;
            }
        }
        if(v<=vt)
        {
            pre[v]=u;
            u=v;
            if(v==vt)
            {
                aug=inf;
                //寻找当前找到的一条路径上的最大流
                for(int i=v; i!=vs; i=pre[i])
                {
                    if(aug>mp[pre[i]][i])aug=mp[pre[i]][i];
                }
                maxflow+=aug;
                //更新残留网络
                for(int i=v; i!=vs; i=pre[i])
                {
                    mp[pre[i]][i]-=aug;
                    mp[i][pre[i]]+=aug;
                }
                u=vs;//从源点开始继续搜
            }
        }
        else
        {
            //找不到可行弧
            int minlevel=vt;
            //寻找与当前点相连接的点中最小的距离标号
            for(v=1; v<=vt; v++)
            {
                if(mp[u][v]>0&&minlevel>level[v])
                {
                    minlevel=level[v];
                }
            }
            gap[level[u]]--;//(更新gap数组)当前标号的数目减1;
            if(gap[level[u]]==0)break;//出现断层
            level[u]=minlevel+1;
            gap[level[u]]++;
            u=pre[u];
        }
    }
    return maxflow;
}

int main()
{
    char s[210];
    int t;
    cin>>t;
    while(t--)
    {
        int n,m;
        cin>>n>>m;
        memset(mp,0,sizeof(mp));
        int x;
        int st=n+m+1;
        int ed=n+m+2;
        int sum=0;
        for (int i=1; i<=m; i++)
        {
            scanf("%d",&x);
            mp[i+n][ed]=x;
            sum+=x;
        }
        for (int i=1; i<=n; i++)
        {
            scanf("%d",&x);
            mp[st][i]=x;
            int k;
            cin>>k;
            for (int j=1; j<=k; j++)
            {
                scanf("%d",&x);
                mp[i][x+n]=1;
            }
        }


        int a=SAP(st,ed);
        if (a==sum)
            printf("Yes\n");
        else printf("No\n");

    }


    return 0;

}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值