hihocoder-1393 二分图的多重匹配(网络流做法)

题意:

n个人m种比赛项目,每个人有若干个自己擅长的项目,但每个人最多只想参加若干个比赛项目(每个人不会参加其不擅长的比赛项目),而每个比赛项目又必须需要若干个比赛人员,给定若干信息,问能否安排一个方案满足上述所有要求。

分析:

裸的二分图的多重匹配,源点S连向A部每个人,容量为每个人最多想参加的项目数目,B部每种项目连向汇点T,容量为每个项目必须需要的人员数目,A部和B部之间就是每个人与其对应擅长的比赛项目进行连边,容量为1。跑一次最大流之后,判断B部所有点到汇点的所有边是否都是满流,如果是则能满足要求,否则不能。

代码:

#include <algorithm>    
#include <iostream>    
#include <string.h>    
#include <cstdio>    
#include <queue>    
using namespace std;    
const int inf = 0x3f3f3f3f;    
const int maxn = 210;    
const int maxm = 21010;  
struct node{int w; int v, next;} edge[maxm];    
int pre[maxn], rec[maxn], head[maxn], gap[maxn], now[maxn];    
int dis[maxn];  
int t, n, m, no, up, sum;    
int S, T;   
queue<int> q;  
inline void add(int u, int v, int w)    
{    
    edge[no].v = v; edge[no].w = w;    
    edge[no].next = head[u]; head[u] = no++;    
    edge[no].v = u; edge[no].w = 0;    
    edge[no].next = head[v]; head[v] = no++;    
}    
inline void pre_init()    
{    
    no = 0;  
    memset(head, -1, sizeof head);    
}    
void init(int S, int T)    
{    
    memset(gap, 0, sizeof gap);    
    memset(dis, 0x3f, sizeof dis);    
    for(int i = 0; i <= up; ++i)     
    now[i] = head[i];  
    while(!q.empty()) q.pop();    
    dis[T] = 0; q.push(T);    
    while(!q.empty())   
    {    
        int tp = q.front(); q.pop();    
        ++gap[dis[tp]];    
        int k = head[tp];    
        while(k != -1)    
        {    
            if(dis[edge[k].v] == inf && edge[k^1].w)    
            {    
                dis[edge[k].v] = dis[tp]+1;    
                q.push(edge[k].v);    
            }    
            k = edge[k].next;    
        }    
    }    
}    
int SAP(int S, int T)    
{  
    int ans = 0, flow = inf;  
    int top = S;    
    pre[S] = S; init(S, T);    
    while(dis[S] < up)  
    {  
        if(top == T)    
        {    
            ans += flow;    
            while(top != S)  
            {    
                edge[rec[top]].w -= flow;    
                edge[rec[top]^1].w += flow;    
                top = pre[top];    
            }    
            flow = inf;    
        }    
        int k = now[top];    
        while(k != -1)    
        {    
            int v = edge[k].v;    
            if(edge[k].w && dis[top] == dis[v]+1)    
            {    
                flow = min(flow, edge[k].w);    
                pre[v] = top; rec[v] = k;    
                now[top] = k; top = v;    
                break;    
            }    
            k = edge[k].next;    
        }    
        if(k == -1)    
        {    
            int mind = up;    
            if(--gap[dis[top]] == 0) break;  
            int k = now[top] = head[top];  
            while(k != -1)    
            {    
                if(edge[k].w && mind>dis[edge[k].v]) mind = dis[edge[k].v];    
                k = edge[k].next;    
            }    
            ++gap[dis[top] = mind+1];    
            top = pre[top];    
        }    
    }    
    return ans;    
}    
void mapping()  
{  
    int x, a, b; 
	sum = 0; 
	for(int i = 1; i <= m; ++i)
	{
		scanf("%d", &x);
		sum += x;
		add(n+i, T, x);
	}
    for(int i = 1; i <= n; ++i)    
    {    
    	scanf("%d", &a);
    	add(S, i, a);
    	scanf("%d", &b);
        for(int j = 1; j <= b; ++j)  
        {
        	scanf("%d", &x);
        	add(i, n+x, 1);
		}
    }  
}  
int main()    
{
    for(scanf("%d", &t); t--;)    
    {    
    	scanf("%d %d", &n, &m);
        up = n+m+2, S = n+m+1, T = n+m+2;  
        pre_init();    
        mapping();
        if(SAP(S, T) == sum) puts("Yes");
        else puts("No"); 
    }    
    return 0;    
}


很显然,求解二分图的多重最优匹配就是用费用流去做,建立源点S到A部所有点的边容量为当前点的限制,费用为0,再建立B部所有点到汇点T的边容量也是当前点的限制,费用为0,中间A、B两部的建边就是二分图之间的边,之后跑一边费用流即可。


继续加油~

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值