hihoCoder 1393: 网络流三·二分图多重匹配
http://hihocoder.com/problemset/problem/1393
描述
学校的秋季运动会即将开始,为了决定参赛人员,各个班又开始忙碌起来。
小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
解题思路:
建立一个超级源点和超级汇点,超级源点到每个学生的权值是每个学生多同时参加的比赛数目,每个比赛到超级汇点的权值是比赛所需要的人的数目,每个学生和擅长的比赛之间建立一条权值为1的边。
求得最大流如果是所有比赛需要的总人数,则输出Yes。
#include <stdio.h>
#include <string.h>
#include <queue>
#include <algorithm>
#define N 220
using namespace std;
int n, m, inf=0x7f7f7f;
int e[N][N], flow[N], pre[N];
int bfs(int s, int t)
{
int u, v;
queue<int>q;
q.push(s);
flow[s] = inf;
while (!q.empty())
{
u = q.front();
q.pop();
if (u == t)
break;
for (v = 0; v <= n + m + 1; v++)
{
if (pre[v] == -1 && e[u][v] && v != s)
{
pre[v] = u;
q.push(v);
flow[v] = min(flow[u], e[u][v]);
}
}
}
if (pre[t] == -1)
return -1;
return flow[t];
}
int EK(int s, int t)
{
int d, p, sum = 0;
while (1)
{
memset(pre, -1, sizeof(pre));
d = bfs(s, t);
if(d==-1)
return sum;
p = t;
while (p != s)
{
e[pre[p]][p] -= d;
e[p][pre[p]] += d;
p = pre[p];
}
sum += d;
}
return sum;
}
int main()
{
int t, u, v, w, sum, i, T;
scanf("%d", &t);
while (t--)
{
sum = 0;
memset(e, 0, sizeof(e));
scanf("%d%d", &n, &m);
for (i = 1; i <= m; i++)
{
scanf("%d", &w);
e[n + i][n + m + 1] = w;
sum += w;
}
for (i = 1; i <= n; i++)
{
scanf("%d%d", &w, &T);
e[0][i] = w;
while (T--)
{
scanf("%d", &v);
e[i][n + v] = 1;
}
}
if (EK(0, n + m + 1) == sum)
printf("Yes\n");
else printf("No\n");
}
return 0;
}