0
点赞
收藏
分享

微信扫一扫

洛谷 P3254——圆桌问题【最大流 & Dinic算法 + 优化 & 另类贪心解法】


​​题目传送门​​

题目描述

假设有来自m 个不同单位的代表参加一次国际会议。每个单位的代表数分别为ri (i =1,2,……,m)。

会议餐厅共有n 张餐桌,每张餐桌可容纳ci (i =1,2,……,n)个代表就餐。

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

对于给定的代表数和餐桌数以及餐桌容量,编程计算满足要求的代表就餐方案。

输入格式

第1 行有2 个正整数m 和n,m 表示单位数,n 表示餐桌数,1<=m<=150, 1<=n<=270。

第2 行有m 个正整数,分别表示每个单位的代表数。

第3 行有n 个正整数,分别表示每个餐桌的容量。

输出格式

如果问题有解,第1 行输出1,否则输出0。接下来的m 行给出每个单位代表的就餐桌号。如果有多个满足要求的方案,只要输出1 个方案。

输入

4 5
4 5 3 5
3 5 2 6 4

输出

1
1 2 4 5
1 2 3 4 5
2 4 5
1 2 3 4 5

题解

  • 比较清爽的最大流(奇水无比
  • 这题建图感觉还是很显然的
  • 首先可以看出一个明显的二分图模型, 单位是一种点, 餐桌是另一种点, 代表数量可以看做是流量.
  • 洛谷 P3254——圆桌问题【最大流 & Dinic算法 + 优化 & 另类贪心解法】_ios 连到所有单位点, 容量为代表数量. 单位点和餐桌点之间两两连一条容量为 洛谷 P3254——圆桌问题【最大流 & Dinic算法 + 优化 & 另类贪心解法】_算法_02 的边代表"同一个单位来的代表不能在同一个餐桌就餐"的限制. 餐桌点再向 洛谷 P3254——圆桌问题【最大流 & Dinic算法 + 优化 & 另类贪心解法】_图论_03
    这样的话, 每一单位流量都代表着一个代表. 它们流经的点和边就代表了它们的特征. 而容量就是对代表的限制…
  • 至于输出方案, 我们只要看从单位点到餐桌点的边有哪些满载了, 一条满载的边洛谷 P3254——圆桌问题【最大流 & Dinic算法 + 优化 & 另类贪心解法】_ios_04 意义就是在最优方案中一个来自 洛谷 P3254——圆桌问题【最大流 & Dinic算法 + 优化 & 另类贪心解法】_最大流_05 代表的单位的代表坐到了 洛谷 P3254——圆桌问题【最大流 & Dinic算法 + 优化 & 另类贪心解法】_最大流_06
    当然如果最大流没有跑满(最大流的值不等于代表数量之和) 的话肯定有代表没被分配出去, 判定无解.

有毒的贪心解法

洛谷 P3254——圆桌问题【最大流 & Dinic算法 + 优化 & 另类贪心解法】_ios_07贪心的策略:

  • 首先将桌子从大到小排个序,再讲单位的规模从大到小排个序。这样有什么用呢?因为单位规模越大就越难满足,所以我们优先考虑他们;
  • 而对于桌子你可以这样想,你桌子数量越多显然更容易满足题意,又因为小桌子很容易坐爆而导致不能用,所以我们优先坐大桌子。

AC-Code

  • Dinic解法:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define

const int inf = 0x3f3f3f3f;
const int maxn = 150 * 270 + 150 + 270;
int n, m;

struct Edge {
int to;
int next;
int val;
}edge[maxn << 1]; // 双向边,开 2 倍数组
int head[maxn];
int cnt; // 边的数量,从 0 开始编号
int depth[maxn]; // 分层图标记深度
int cur[maxn]; // 当前弧优化,记录当前点 u 循环到了哪一条边
void add(int u, int v, int w) {
edge[cnt].to = v;
edge[cnt].next = head[u];
edge[cnt].val = w;
head[u] = cnt++;
}

// bfs分层图
bool bfs(int s, int t) {
queue<int>q;
memset(depth, 0, sizeof depth);
depth[s] = 1; // 源点深度为 1
cur[s] = head[s];
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if (edge[i].val > 0 && depth[v] == 0) { // 如果残量不为0,且 v 还未分配深度
depth[v] = depth[u] + 1;
cur[v] = head[v]; //------当前弧优化,注意在bfs里,这样做到了“按需赋值”,因为Dinic本来上界就松得一匹, BFS的过程中不连通的点根本就不用再管了...
if (v == t) // -----分层图汇点优化:遇到汇点直接返回------
return true;
q.push(v);
}
}
}
return depth[t]; // 汇点深度为 0:不存在分层图,返回false;
// 非 0 :存在增广路,返回true
}

// dfs寻找增广路
int dfs(int u, int flow, int t) {
if (u == t || flow <= 0) // 到达汇点
return flow;
int rest = flow;
for (int i = cur[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if (depth[v] == depth[u] + 1 && edge[i].val != 0) { // 满足分层图、残量>0 两个条件
int k = dfs(v, min(rest, edge[i].val), t); // 向下增广
if (k < 0) // ------无用点优化-----
depth[v] = 0;
rest -= k;
edge[i].val -= k; // 正向边减
edge[i ^ 1].val += k; // 反向边加
if (rest <= 0) //------剩余量优化:在进行增广的时候,如果该节点已经没有流量,直接退出------
break;
}
}
return flow - rest; // flow:推送量,rest:淤积量,flow - rest:接受量/成功传递量
}
int Dinic(int s, int t) {
int ans = 0;
while (bfs(s, t)) {
ans += dfs(s, inf, t);
}
return ans;
}
int main() {
ios;
while (cin >> m >> n) {
cnt = 0;
memset(head, -1, sizeof head);
int peoplenum = 0;
for (int i = 1, k; i <= m; ++i) {
cin >> k;
peoplenum += k;
add(0, i, k);
add(i, 0, 0);
}
for (int i = 1, k; i <= n; ++i) {
cin >> k;
for (int j = 1; j <= m; ++j) {
add(j, m + i, 1);
add(m + i, j, 0);
}
add(m + i, m + n + 1, k);
add(m + n + 1, m + i, 0);
}
if (Dinic(0, m + n + 1) == peoplenum) {
cout << 1 << endl;
for (int i = 1; i <= m; ++i) {
for (int j = head[i]; ~j; j = edge[j].next) {
int v = edge[j].to;
if (v != 0 && !edge[j].val)
cout << v - m << " ";
}
cout << endl;
}
}
else
cout << 0 << endl;
}
}

  • 贪心解法

#include <bits/stdc++.h>
using namespace std;
const int maxn = 305, maxm = 205;
int d[maxn], z[maxn], wow[maxn];
int m ,n;
struct lpl
{
int num, data;
}desk[maxn], people[maxm];
bool operator < (lpl a, lpl b)
{
return a.data > b.data;
}
vector<int> ld[maxm];
inline void putit()
{
scanf("%d%d", &m, &n);
for(int i = 1; i <= m; ++i)
{
scanf("%d", &d[i]);
people[i].num = i; people[i].data = d[i];
}
for(int i = 1; i <= n; ++i)
{
scanf("%d", &z[i]);
desk[i].num = i; desk[i].data = z[i];
}
}
inline void print()
{
printf("1\n");
for(int i = 1; i <= m; ++i)
{
for(int j = 0; j < ld[i].size(); ++j)
{
printf("%d ", ld[i][j]);
}
printf("\n");
}
}
int main()
{
putit();
sort(desk + 1, desk + n + 1);
sort(people + 1, people + m + 1);
for(int i = 1; i <= m; ++i)
{
int qwe = 1;
for(int j = 0; j < people[i].data; ++j)
{
bool flag = false;
for(int t = qwe; t <= n; ++t)
{
if(wow[ desk[t].num ] < desk[t].data)
{
wow[ desk[t].num ]++; qwe = t + 1;
flag = true; ld[ people[i].num ].push_back(desk[t].num);
break;
}
}
if(flag == false)
{
printf("0");
return 0;
}
}
}
print();
return 0;
}


举报

相关推荐

0 条评论