网络流 - 二分图的多重匹配问题 - 圆桌问题 - 网络流24题 - 洛谷 P3254

网络流 - 二分图的多重匹配问题 - 圆桌问题 - 网络流24题 - 洛谷 P3254

题目描述

有 来 自 m 个 不 同 单 位 的 代 表 参 加 一 次 国 际 会 议 。 第 i 个 单 位 派 出 了 r i 个 代 表 。 有来自 m 个不同单位的代表参加一次国际会议。第 i 个单位派出了 r_i 个代表。 miri

会 议 的 餐 厅 共 有 n 张 餐 桌 , 第 i 张 餐 桌 可 容 纳 c i 个 代 表 就 餐 。 会议的餐厅共有 n张餐桌,第 i 张餐桌可容纳 c_i个代表就餐。 nici

为 了 使 代 表 们 充 分 交 流 , 希 望 从 同 一 个 单 位 来 的 代 表 不 在 同 一 个 餐 桌 就 餐 。 请 给 出 一 个 满 足 要 求 的 代 表 就 餐 方 案 。 为了使代表们充分交流,希望从同一个单位来的代表不在同一个餐桌就餐。请给出一个满足要求的代表就餐方案。 使

输入格式

输 入 的 第 一 行 是 用 空 格 隔 开 的 两 个 整 数 , 分 别 代 表 单 位 的 个 数 m 和 餐 桌 的 个 数 n 。 输入的第一行是用空格隔开的两个整数,分别代表单位的个数 m 和餐桌的个数 n。 mn

第 二 行 有 m 个 用 空 格 隔 开 的 整 数 , 第 i 个 整 数 代 表 第 i 个 单 位 的 代 表 人 数 r i 。 第二行有 m 个用空格隔开的整数,第 i 个整数代表第 i 个单位的代表人数 r_i 。 miiri

第 三 行 有 n 个 用 空 格 隔 开 的 整 数 , 第 i 个 整 数 代 表 第 i 张 餐 桌 能 容 纳 的 人 数 c i 。 第三行有 n 个用空格隔开的整数,第 i 个整数代表第 i 张餐桌能容纳的人数 c_i。 niici

输出格式

本题存在 Special Judge。

请 输 出 是 否 存 在 满 足 要 求 的 就 餐 方 案 , 若 存 在 , 请 给 出 任 意 一 种 可 行 的 方 案 。 请输出是否存在满足要求的就餐方案,若存在,请给出任意一种可行的方案。

输 出 的 第 一 行 是 一 个 非 0 即 1 的 整 数 , 若 存 在 方 案 则 输 出 1 , 否 则 输 出 0 。 输出的第一行是一个非 0 即 1 的整数,若存在方案则输出 1,否则输出 0。 0110

若 存 在 方 案 , 则 对 于 第 2 到 第 ( m + 1 ) 行 , 在 第 ( i + 1 ) 行 输 出 r i 个 整 数 , 代 表 第 i 个 单 位 的 代 表 就 餐 的 餐 桌 编 号 。 若存在方案,则对于第 2 到第 (m + 1)行,在第 (i + 1) 行输出 r_i 个整数,代表第 i 个单位的代表就餐的餐桌编号。 2(m+1)(i+1)rii

输入输出样例

输入 #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%1m1501n2701ri,ci103

分析:

二 分 图 的 多 重 匹 配 问 题 。 二分图的多重匹配问题。

即 同 一 点 可 以 与 不 同 的 点 匹 配 。 即同一点可以与不同的点匹配。

题 意 即 : 两 组 点 之 间 进 行 匹 配 , 每 个 点 都 有 匹 配 次 数 的 上 限 值 , 求 最 大 匹 配 。 题意即:两组点之间进行匹配,每个点都有匹配次数的上限值,求最大匹配。

从 源 点 S 向 一 侧 的 点 ( 代 表 ) 连 接 权 值 为 r i 的 正 向 边 , 权 值 为 0 的 反 向 边 。 从源点S向一侧的点(代表)连接权值为r_i的正向边,权值为0的反向边。 Sri0

从 另 一 侧 点 ( 餐 桌 ) 向 汇 点 T 连 接 一 条 权 值 为 c i 的 正 向 边 , 权 值 为 0 的 反 向 边 。 从另一侧点(餐桌)向汇点T连接一条权值为c_i的正向边,权值为0的反向边。 Tci0

从 代 表 中 的 所 有 点 向 餐 桌 中 的 所 有 点 连 接 一 条 权 值 为 1 的 正 向 边 , 权 值 为 0 的 反 向 边 , 这 确 保 了 同 一 个 点 只 能 与 另 一 个 点 匹 配 一 次 。 从代表中的所有点向餐桌中的所有点连接一条权值为1的正向边,权值为0的反向边,这确保了同一个点只能与另一个点匹配一次。 10

然 后 在 建 立 好 的 残 留 网 络 中 跑 最 大 流 。 然后在建立好的残留网络中跑最大流。

若 最 大 流 与 人 数 总 和 相 等 , 则 说 明 所 有 的 人 都 能 安 排 入 座 。 若最大流与人数总和相等,则说明所有的人都能安排入座。

然 后 对 每 个 点 ( 代 表 ) , 遍 历 其 相 邻 的 边 , 若 某 条 边 满 流 , 则 输 出 这 条 边 的 端 点 编 号 。 然后对每个点(代表),遍历其相邻的边,若某条边满流,则输出这条边的端点编号。

注 意 , 编 号 要 减 去 偏 移 量 ( 这 取 决 于 建 图 的 时 候 的 顺 序 ) 。 注意,编号要减去偏移量(这取决于建图的时候的顺序)。

代码:

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值