二分图多重匹配

题目链接:

http://hihocoder.com/problemset/problem/1393

分析:

很简单的一道匹配题目

用流可以解决一切匹配(某大牛说的,,)

所以舍弃KM转到流的怀抱吧


将容量视为人数

人和比赛之间的容量置为1

比赛和汇点之间的容量置为这个比赛需要的人数

源点和人之间自然是人能参加比赛的个数(当作TA使用了分身术吧)


#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
#define maxn 555

int N,M;
int S,T;
int sum;
const int INF = 1e9;
struct Edge{
    int from,to,cap,flow;
     Edge(int f,int t,int c,int ff)
        :from(f),to(t),cap(c),flow(ff) {};
};
vector<Edge> edges;
vector<int> G[maxn];
int iter[maxn] ,level[maxn];
int vis[maxn];

void add_Edge(int from,int to,int cap)
{
    edges.push_back( Edge(from,to,cap,0) );
    edges.push_back( Edge(to,from,0,0) );
     int sz = edges.size();
    G[from].push_back(sz-2);
    G[to].push_back(sz-1);
}
bool BFS( )
{
//	printf("!\n");
	memset(vis,false,sizeof(vis));
	level[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 = edges[G[u][i]];
			if(!vis[e.to]&&e.cap > e.flow)
			{
				vis[e.to] = 1;
				level[e.to] = level[u] + 1;
				Q.push(e.to);
			}
		}
	}
	return vis[T];
}

int DFS(int u ,int a)
{
//	printf("@\n");
	if(u == T || a == 0)
		return a;
	int f , flow = 0;
	for(int& i = iter[u]; i < (int)G[u].size(); ++i)// ?
	{
		Edge& e = edges[G[u][i]];
		if(level[e.to] == level[u] + 1 && (f = DFS(e.to , min(a,e.cap - e.flow))) > 0 )
		{
			e.flow += f;
			edges[G[u][i]^1].flow -= f;
			return f;
		 }
	}
	return flow;
}
int Dinic( )
{
	int res = 0 ;
	while(BFS())
	{
		memset(iter,0,sizeof(iter));
		res += DFS(S,INF);
	}
	return res;
}
void init()
{
    S = 0  , T = N + M + 1;
    for(int i = 0 ; i <= T ; ++i)
        G[i].clear();
        edges.clear();

}

int main()
{
    int t;cin>>t;
    while(t--)
    {
        cin >> N >> M;
        init();
        sum = 0;
        for(int i = 1 ; i <= M ; ++i)
        {
            int cap;
            scanf("%d",&cap);
            sum+=cap;
            add_Edge(N+i,T,cap);
        }
        for(int i = 1 ; i <= N ; ++i)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            add_Edge(0,i,a);
            for(int j=0;j<b;j++)
            {
                int to;
                scanf("%d",&to);
                add_Edge(i,N+to,1);
            }
        }

        if( sum == Dinic() )printf("Yes\n");
        else printf("No\n");
    }
}



 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值