题意:
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两部的建边就是二分图之间的边,之后跑一边费用流即可。
继续加油~