网络流 - 二分图的多重匹配问题 - 圆桌问题 - 网络流24题 - 洛谷 P3254
题目描述
有 来 自 m 个 不 同 单 位 的 代 表 参 加 一 次 国 际 会 议 。 第 i 个 单 位 派 出 了 r i 个 代 表 。 有来自 m 个不同单位的代表参加一次国际会议。第 i 个单位派出了 r_i 个代表。 有来自m个不同单位的代表参加一次国际会议。第i个单位派出了ri个代表。
会 议 的 餐 厅 共 有 n 张 餐 桌 , 第 i 张 餐 桌 可 容 纳 c i 个 代 表 就 餐 。 会议的餐厅共有 n张餐桌,第 i 张餐桌可容纳 c_i个代表就餐。 会议的餐厅共有n张餐桌,第i张餐桌可容纳ci个代表就餐。
为 了 使 代 表 们 充 分 交 流 , 希 望 从 同 一 个 单 位 来 的 代 表 不 在 同 一 个 餐 桌 就 餐 。 请 给 出 一 个 满 足 要 求 的 代 表 就 餐 方 案 。 为了使代表们充分交流,希望从同一个单位来的代表不在同一个餐桌就餐。请给出一个满足要求的代表就餐方案。 为了使代表们充分交流,希望从同一个单位来的代表不在同一个餐桌就餐。请给出一个满足要求的代表就餐方案。
输入格式
输 入 的 第 一 行 是 用 空 格 隔 开 的 两 个 整 数 , 分 别 代 表 单 位 的 个 数 m 和 餐 桌 的 个 数 n 。 输入的第一行是用空格隔开的两个整数,分别代表单位的个数 m 和餐桌的个数 n。 输入的第一行是用空格隔开的两个整数,分别代表单位的个数m和餐桌的个数n。
第 二 行 有 m 个 用 空 格 隔 开 的 整 数 , 第 i 个 整 数 代 表 第 i 个 单 位 的 代 表 人 数 r i 。 第二行有 m 个用空格隔开的整数,第 i 个整数代表第 i 个单位的代表人数 r_i 。 第二行有m个用空格隔开的整数,第i个整数代表第i个单位的代表人数ri。
第 三 行 有 n 个 用 空 格 隔 开 的 整 数 , 第 i 个 整 数 代 表 第 i 张 餐 桌 能 容 纳 的 人 数 c i 。 第三行有 n 个用空格隔开的整数,第 i 个整数代表第 i 张餐桌能容纳的人数 c_i。 第三行有n个用空格隔开的整数,第i个整数代表第i张餐桌能容纳的人数ci。
输出格式
本题存在 Special Judge。
请 输 出 是 否 存 在 满 足 要 求 的 就 餐 方 案 , 若 存 在 , 请 给 出 任 意 一 种 可 行 的 方 案 。 请输出是否存在满足要求的就餐方案,若存在,请给出任意一种可行的方案。 请输出是否存在满足要求的就餐方案,若存在,请给出任意一种可行的方案。
输 出 的 第 一 行 是 一 个 非 0 即 1 的 整 数 , 若 存 在 方 案 则 输 出 1 , 否 则 输 出 0 。 输出的第一行是一个非 0 即 1 的整数,若存在方案则输出 1,否则输出 0。 输出的第一行是一个非0即1的整数,若存在方案则输出1,否则输出0。
若 存 在 方 案 , 则 对 于 第 2 到 第 ( m + 1 ) 行 , 在 第 ( i + 1 ) 行 输 出 r i 个 整 数 , 代 表 第 i 个 单 位 的 代 表 就 餐 的 餐 桌 编 号 。 若存在方案,则对于第 2 到第 (m + 1)行,在第 (i + 1) 行输出 r_i 个整数,代表第 i 个单位的代表就餐的餐桌编号。 若存在方案,则对于第2到第(m+1)行,在第(i+1)行输出ri个整数,代表第i个单位的代表就餐的餐桌编号。
输入输出样例
输入 #1
4 5
4 5 3 5
3 5 2 6 4
输出 #1
1
1 2 4 5
1 2 3 4 5
2 4 5
1 2 3 4 5
说明/提示
【数据规模与约定】
对 于 100 % 的 数 据 , 保 证 1 ≤ m ≤ 150 , 1 ≤ n ≤ 270 , 1 ≤ r i , c i ≤ 1 0 3 。 对于 100\% 的数据,保证 1 \leq m \leq 150,1 \leq n \leq 270,1 \leq r_i, c_i \leq 10^3 。 对于100%的数据,保证1≤m≤150,1≤n≤270,1≤ri,ci≤103。
分析:
二 分 图 的 多 重 匹 配 问 题 。 二分图的多重匹配问题。 二分图的多重匹配问题。
即 同 一 点 可 以 与 不 同 的 点 匹 配 。 即同一点可以与不同的点匹配。 即同一点可以与不同的点匹配。
题 意 即 : 两 组 点 之 间 进 行 匹 配 , 每 个 点 都 有 匹 配 次 数 的 上 限 值 , 求 最 大 匹 配 。 题意即:两组点之间进行匹配,每个点都有匹配次数的上限值,求最大匹配。 题意即:两组点之间进行匹配,每个点都有匹配次数的上限值,求最大匹配。
从 源 点 S 向 一 侧 的 点 ( 代 表 ) 连 接 权 值 为 r i 的 正 向 边 , 权 值 为 0 的 反 向 边 。 从源点S向一侧的点(代表)连接权值为r_i的正向边,权值为0的反向边。 从源点S向一侧的点(代表)连接权值为ri的正向边,权值为0的反向边。
从 另 一 侧 点 ( 餐 桌 ) 向 汇 点 T 连 接 一 条 权 值 为 c i 的 正 向 边 , 权 值 为 0 的 反 向 边 。 从另一侧点(餐桌)向汇点T连接一条权值为c_i的正向边,权值为0的反向边。 从另一侧点(餐桌)向汇点T连接一条权值为ci的正向边,权值为0的反向边。
从 代 表 中 的 所 有 点 向 餐 桌 中 的 所 有 点 连 接 一 条 权 值 为 1 的 正 向 边 , 权 值 为 0 的 反 向 边 , 这 确 保 了 同 一 个 点 只 能 与 另 一 个 点 匹 配 一 次 。 从代表中的所有点向餐桌中的所有点连接一条权值为1的正向边,权值为0的反向边,这确保了同一个点只能与另一个点匹配一次。 从代表中的所有点向餐桌中的所有点连接一条权值为1的正向边,权值为0的反向边,这确保了同一个点只能与另一个点匹配一次。
然 后 在 建 立 好 的 残 留 网 络 中 跑 最 大 流 。 然后在建立好的残留网络中跑最大流。 然后在建立好的残留网络中跑最大流。
若 最 大 流 与 人 数 总 和 相 等 , 则 说 明 所 有 的 人 都 能 安 排 入 座 。 若最大流与人数总和相等,则说明所有的人都能安排入座。 若最大流与人数总和相等,则说明所有的人都能安排入座。
然 后 对 每 个 点 ( 代 表 ) , 遍 历 其 相 邻 的 边 , 若 某 条 边 满 流 , 则 输 出 这 条 边 的 端 点 编 号 。 然后对每个点(代表),遍历其相邻的边,若某条边满流,则输出这条边的端点编号。 然后对每个点(代表),遍历其相邻的边,若某条边满流,则输出这条边的端点编号。
注 意 , 编 号 要 减 去 偏 移 量 ( 这 取 决 于 建 图 的 时 候 的 顺 序 ) 。 注意,编号要减去偏移量(这取决于建图的时候的顺序)。 注意,编号要减去偏移量(这取决于建图的时候的顺序)。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 150 + 270 + 10, M = (150 * 270 + 150 + 270 + 10) * 2, inf = 1e9;
int n, m, S, T;
int e[M], ne[M], f[M], h[N], idx;
int q[N], cur[N], d[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
bool bfs()
{
int hh = 0, tt = -1;
memset(d, -1, sizeof d);
d[S] = 0, q[++ tt] = S, cur[S] = h[S];
while(hh <= tt)
{
int u = q[hh ++];
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(d[v] == -1 && f[i] > 0)
{
d[v] = d[u] + 1;
cur[v] = h[v];
if(v == T) return true;
q[++ tt] = v;
}
}
}
return false;
}
int find(int u, int limit)
{
if(u == T) return limit;
int flow = 0, t;
for(int i = cur[u]; ~i && flow < limit; i = ne[i])
{
int v = e[i];
cur[u] = i;
if(d[v] == d[u] + 1 && f[i] > 0)
{
t = find(v, min(f[i], limit - flow));
if(!t) d[v] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int res = 0, flow;
while(bfs()) while(flow = find(S, inf)) res += flow;
return res;
}
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = 150 + 270 + 1;
memset(h, -1, sizeof h);
int sum = 0;
for(int i = 1; i <= n; i ++)
{
int r;
scanf("%d", &r);
sum += r;
add(S, i, r), add(i, S, 0);
}
for(int i = n + 1; i <= n + m; i ++)
{
int c;
scanf("%d", &c);
add(i, T, c), add(T, i, 0);
}
for(int i = 1; i <= n; i ++)
for(int j = n + 1; j <= n + m; j ++)
add(i, j, 1), add(j, i, 0);
int res = dinic();
if(res == sum)
{
puts("1");
for(int i = 1; i <= n; i ++)
{
for(int j = h[i]; ~j; j = ne[j])
if(!f[j])
printf("%d ", e[j] - n);
puts("");
}
}
else puts("0");
return 0;
}