2187. 星际转移问题

题目

由于人类对自然资源的消耗,人们意识到大约在 \(2300\) 年之后,地球就不能再居住了。
于是在月球上建立了新的绿地,以便在需要时移民。
令人意想不到的是,\(2177\) 年冬由于未知的原因,地球环境发生了连锁崩溃,人类必须在最短的时间内迁往月球。
现有 \(n\) 个太空站(编号 \(1 \sim n\))位于地球与月球之间,且有 \(m\) 艘公共交通太空船在其间来回穿梭。
每个太空站可容纳无限多的人,而每艘太空船 \(i\) 只可容纳 \(H[i]\) 个人.
每艘太空船将周期性地停靠一系列的太空站,例如:\((1,3,4)\) 表示该太空船将周期性地停靠太空站 \(134134134…\)
每一艘太空船从一个太空站驶往任一太空站耗时均为 \(1\)
人们只能在太空船停靠太空站(或月球、地球)时上、下船。
初始时所有人全在地球上,太空船全在初始站,即行驶周期中的第一个站。
试设计一个算法,找出让所有人尽快地全部转移到月球上的运输方案。

输入格式

\(1\) 行有 \(3\) 个正整数 \(n\)(太空站个数),\(m\)(太空船个数)和 \(k\)(需要运送的地球上的人的个数)。
接下来的 \(m\) 行给出太空船的信息。第 \(i+1\) 行说明太空船 \(p_i\)。第 \(1\) 个数表示 \(p_i\) 可容纳的人数 \(H[p_i]\);第 \(2\) 个数表示 \(p_i\) 一个周期停靠的太空站个数 \(r\);随后 \(r\) 个数是停靠的太空站的编号 \((S_{i1},S_{i2},…,S_{ir})\),地球用 \(0\) 表示,月球用 \(-1\) 表示
时刻 \(0\) 时,所有太空船都在初始站,然后开始运行。
在时刻 \(1,2,3…\) 等正点时刻各艘太空船停靠相应的太空站。
人只有在 \(0,1,2…\) 等正点时刻才能上下太空船。

输出格式

输出让所有人尽快地全部转移到月球上的最短用时。
如果无解,则输出 \(0\)

数据范围

\(1 \le n \le 13\),
\(1 \le m \le 20\),
\(1 \le k \le 50\),
\(1 \le r \le n+2\)

输入样例

2 2 1
1 3 0 1 2
1 3 1 2 -1

输出样例:

5

学了一段时间的网络流了吧, 感觉这道题还是比较好的, 这个建图很考验思维, 首先无解的情况很好判断, 我们使用并查集, 把每个飞船所能到达的点都并到一起, 如果最后起点和终点在同一个集合中, 那么就是有解的情况 。那我们建图用分层图的做法, 把每一天的空间站都当成一个节点。 建图的细节是, 所有人显然可以在空间站停留, 那么从day-1的第i个节点到day的第i个节点显然可以连一条无穷大的边, 再可以按照飞船的航行路线把day-1的节点连向day的节点, S只能连第0天的地球, 而每一天的月球都需要和T连一条边。 对于天数, 我们需要从小到大枚举, 为什么不二分枚举, 如果二分的话, 我们可能会涉及到删边操作,非常不好写。

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
const int M = 2e6 + 10;

template < typename T > inline void read(T &x) {
	x = 0; T ff = 1, ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-') ff = -1;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = (x << 1) + (x << 3) + (ch ^ 48);
		ch = getchar();
	}
	x *= ff;
} 

int n, m, k, S, T, fa[25];
int tot = -1, lin[N];
int d[N], cur[N];
struct ship {
	int len, r, s[30];
}ships[30]; 
struct edge {
	int y, c, next;
}e[M];

inline int getfa(int x) {
	return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}

inline int get(int x, int day) {
	return (n + 2) * day + x;
}

inline void add(int xx, int yy, int cc) {
	e[++tot].y = yy; e[tot].c = cc; e[tot].next = lin[xx]; lin[xx] = tot;
	e[++tot].y = xx; e[tot].c = 0; e[tot].next = lin[yy]; lin[yy] = tot;
}

inline bool bfs() {
	queue < int > q;
	memset(d, -1, sizeof(d));
	q.push(S); d[S] = 0; cur[S] = lin[S];
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		for (int i = lin[x]; ~i; i = e[i].next) {
			int y = e[i].y;
			if (d[y] == -1 && e[i].c) {
				d[y] = d[x] + 1;
				cur[y] = lin[y];
				if (y == T) return true;
				q.push(y);
			}
		}
	}
	return false;
}

inline int find(int x, int lim) {
	if (x == T) return lim;
	int flow = 0;
	for (int i = cur[x]; ~i && flow < lim; i = e[i].next) {
		int y = e[i].y;
		if(d[y] == d[x] + 1 && e[i].c) {
			int t = find(y, min(e[i].c, lim - flow));
			if (!t) d[y] = -1;
			e[i].c -= t; e[i ^ 1].c += t; flow += t;
		}
	}
	return flow;
}

inline int dinic() {
	int r = 0, flow;
	while (bfs()) while (flow = find(S, INF)) r += flow;
	return r;
}

int main() {
	read(n), read(m), read(k);
	memset(lin, -1, sizeof(lin));
	for (int i = 0; i <= n + 1; ++i) fa[i] = i;
	for (int i = 1; i <= m; ++i) {
		read(ships[i].len);
		read(ships[i].r);
		for (int j = 0; j < ships[i].r; ++j) {
			int id; read(id);
			if (id == -1) id = n + 1;
			ships[i].s[j] = id;
			if (j) {
				int x = ships[i].s[j - 1];
				int u = getfa(x), v = getfa(id);
				fa[u] = v;
			}
		}
	}
	if (getfa(0) != getfa(n + 1)) puts("0");
	else {
		S = N - 1, T = N - 2;
		add(S, 0, INF);
		add(n + 1, T, INF);
		int day = 1, res = 0;
		while (true) {
			for (int i = 0; i <= n + 1; ++i) 
				add(get(i, day - 1), get(i, day), INF);
			add(get(n + 1, day), T, INF);
			for (int i = 1; i <= m; ++i) {
				int r = ships[i].r;
				add(get(ships[i].s[(day - 1) % r], day - 1), get(ships[i].s[day % r], day), ships[i].len);
			}
			res += dinic();
			if (res >= k) break;
			++day;
		}
		printf("%d\n", day);
	}
	return 0;
} 

posted @ 2021-11-08 14:46  海边微风起  阅读(42)  评论(0编辑  收藏  举报