hihoCoder 1393 网络流三·二分图多重匹配(Dinic求二分图最大多重匹配)

时间:2023-12-20 20:51:44

#1393 : 网络流三·二分图多重匹配

时间限制: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

题目链接:hihoCoder 1393

要用网络流做二分图的多重匹配就要知道二分匹配的边的作用,至少先搞懂普通二分匹配是怎么回事

先回想一下二分图的普通最大匹配是如何建图的:源点到$Xi$的容量边其实是用来表示$Xi$最多可以匹配多少个人;从$Xi$到$Yi$的容量边表示$Xi$是否能与$Yi$匹配;从$Yi$到汇点的容量边表示Yi最多可以被多少个人匹配。显然普通二分图以上容量均为1,那么参照以上的原理,多重匹配就是把最左和最右改一下即可,中间的为什么不用改呢?一个人对于一个项目的作用只有1,不可能达到两个人的效果,因此容量限制还是只有1,话说Dinic比朴素Ford_Fulkerson快了很多……可能是FF写的搓吧(dinic也没好看到哪里去)

hihoCoder 1393 网络流三·二分图多重匹配(Dinic求二分图最大多重匹配)

最近要期末考了,抓紧时间复习(预习- -|||),基本就不刷题了颓废一下,考完再刷……

代码:

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 210;
const int M = 10200;
struct edge
{
int to, nxt;
int cap;
};
edge E[M << 1];
int head[N], tot;
int d[N]; void init()
{
CLR(head, -1);
tot = 0;
}
inline void add(int s, int t, int cap)
{
E[tot].to = t;
E[tot].cap = cap;
E[tot].nxt = head[s];
head[s] = tot++; E[tot].to = s;
E[tot].cap = 0;
E[tot].nxt = head[t];
head[t] = tot++;
}
int bfs(int s, int t)
{
CLR(d, INF);
d[s] = 0;
queue<int>Q;
Q.push(s);
while (!Q.empty())
{
int now = Q.front();
Q.pop();
for (int i = head[now]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == INF && E[i].cap > 0)
{
d[v] = d[now] + 1;
if (v == t)
return 1;
Q.push(v);
}
}
}
return d[t] != INF;
}
int dfs(int s, int t, int f)
{
if (s == t || !f)
return f;
int ret = 0;
for (int i = head[s]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == d[s] + 1 && E[i].cap > 0)
{
int dx = dfs(v, t, min(f, E[i].cap));
if (dx > 0)
{
E[i].cap -= dx;
E[i ^ 1].cap += dx;
ret += dx;
f -= dx;
if (!f)
break;
}
}
}
if (!ret)
d[s] = -1;
return ret;
}
int dinic(int s, int t)
{
int ret = 0;
while (bfs(s, t))
ret += dfs(s, t, INF);
return ret;
}
int main(void)
{
int n, m, i, d, k, a, b;
int tcase;
scanf("%d", &tcase);
while (tcase--)
{
init();
int sum = 0;
scanf("%d%d", &n, &m);
int S = 0, T = n + m + 1;
for (i = 1; i <= m; ++i)
{
int ned;
scanf("%d", &ned);
sum += ned;
add(n + i, T, ned);
}
for (i = 1; i <= n; ++i)
{
scanf("%d%d", &a, &b);
add(S, i, a);
while (b--)
{
scanf("%d", &k);
add(i, n + k, 1);
}
}
puts(dinic(S, T) == sum ? "Yes" : "No");
}
return 0;
}