1 条题解
-
0
自动搬运
来自洛谷,原作者为

hellhell
路漫漫其修远兮 吾将上下而求索搬运于
2025-08-24 22:32:10,当前版本为作者最后更新于2021-11-11 21:19:14,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
[CEOI2005] Depot Rearrangement
题目描述
一家公司经营着 个店铺,每个店铺都销售 种不同的产品。该公司有一个大型仓库,产品在运送到商店之前在都会那里进行包装。每家商店将会收到相同数量的每种产品。因此,该公司将一定数量的相应产品分别包装到一个集装箱中,并用产品标识符标记该集装箱。产品由 到 的数字标识。
因此,在包装结束后,仓库中将会有 个集装箱,并且正好 个集装箱贴有每种产品的对应标签。由于该仓库位于一个狭窄的建筑物内,所以集装箱排成了一排。但为了加快配送速度,仓库经理想要重新排列集装箱。由于将产品配送到商店是通过向每个商店发出一辆卡车来实现的,并且每辆卡车运载每种产品的一个集装箱,其合适的安排如下。该行最前部分 个集装箱必须贴有不同的产品标签,该行的第二部分 个集装箱必须贴有不同的产品标签,依此类推。
不幸的是,在这一行的尽头只有一个空闲的地方可以放置一个集装箱。因此,必须通过依次拿起集装箱并将其移动到空闲位置来进行重新排列。重新排列后,空闲位置也必须在行的末尾。目标是通过最少的移动以实现所需的重新排列。
计算需要最少移动多少次使得达成目标重排,输出最少的移动次数和具体的操作方案,每一步操作是形如 的二元组,表示将 位置上的集装箱移动到位置 上。
样例输入:
5 6 4 1 3 1 6 5 2 3 2 3 5 6 2 1 4 5 6 4 1 3 2 4 5 5 1 2 3 4 6 6样例输出:
8 9 31 18 9 10 18 4 10 31 4 30 31 24 30 31 24思路分析
不难发现,对于每一部分的集装箱,同种类只出现过一次的集装箱我们是不会动它的,我们会移动的是同种类出现次数大于 的集装箱。
设 表示 区间内,种类为 的集装箱出现的次数。
考虑构造二分图,左边点集为 表示第 段长度为 的区间,右边点集为 表示集装箱的种类。
若 ,则在图中从 向 连接 条有向边。
若 ,则在图中从 向 连接一条有向边。
对样例建立二分图

通过这样的方法构造出的二分图,其中一条从左到右再到左的路径为 ,表示将 中种类为 的集装箱放到 的空位中。每次被放入集装箱的一段区间,一定要保证存在一个空位,为了保证操作次数最小,我们要尽可能地让 变成空地的次数最小。
每当我们在图中走完一条回路时, 会为空,所以我们需要让找到的回路条数尽可能地少。也就是对于图中的每一个极大联通子图求一遍欧拉回路。然后按照欧拉回路构造方案即可。
可以证明图中每个子图一定存在欧拉回路。因为我们建边是根据 来建边的,最终对集装箱安排好位置后,每一部分一定都有每种集装箱各 个,所以初始状态,每多一个相同的种类的集装箱,就会少一种不同种类的集装箱。也就是每连接一条 的边同时也会伴随着连接一条 的边。有向图中,每一个点的入度与出度都相等,则一定存在欧拉回路。
具体的构造方案为,对于每一个极大联通子图任选一个点当起点,求欧拉回路。
记录经过的边的编号序列,倒序遍历序列,每遇到 的边就把 移到空地上,同时更新空地的位置。
为什么要倒序枚举序列?每次的移动相当于在图中经过 ,每次移动都要保证 想移动到的位置 是空的,那么倒序枚举就可以保证 一定是空的了,因为我们在移动 中的集装箱时,上一步一定是移动了 中的集装箱。 第一次移动集装箱,将集装箱移动到 上。最后再将 位置上的集装箱移动到现在的空地即可。
对于一个具有 条边的极大联通子图,所需的操作次数为 。
空间只有 ,实现时,记录每个集装箱位置的数组,要用 vector 来存。
代码实现
#include<bits/stdc++.h> #define int long long using namespace std; inline int read() { int f = 1; int res = 0; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { res = res * 10 + ch - '0'; ch = getchar(); } return res * f; } const int maxn = 410; int n, m; vector < int > pos[maxn][maxn];//用 vector 实现,不然会 MLE int tot[maxn][maxn]; struct node { int from; int to; int next; } edge[maxn * maxn * 2]; int head[maxn << 1]; int cnt; void add(int u, int v) { edge[++cnt].to = v; edge[cnt].from = u; edge[cnt].next = head[u]; head[u] = cnt; } int que[maxn * maxn * 2], tag; bool vis[maxn * maxn * 2]; void dfs(int now) { for (int i = head[now]; i; i = edge[i].next) { if (vis[i]) continue; int v = edge[i].to; vis[i] = true; dfs(v); que[++tag] = i; } } struct ANS { int x; int y; } ans[maxn * maxn + maxn]; int len; signed main() { n = read(), m = read(); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { int x = read(); ++tot[i][x]; pos[i][x].push_back((i - 1) * m + j); } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { for (int k = 1; k < tot[i][j]; k++) { add(i, j + n); } if (tot[i][j] < 1) add(j + n, i); } } for (int i = n + 1; i <= n + m; i++) { tag = 0; dfs(i); int to = n * m + 1; for (int i = 1; i <= tag; i++) { int u = edge[que[i]].from; int v = edge[que[i]].to; if (u <= n) { ans[++len].x = pos[u][v - n][--tot[u][v - n]]; ans[len].y = to; to = ans[len].x; } } if (tag) { ans[++len].x = n * m + 1; ans[len].y = to; } } printf("%lld\n", len); for (int i = 1; i <= len; i++) { printf("%lld %lld\n", ans[i].x, ans[i].y); } return 0; }
- 1
信息
- ID
- 7017
- 时间
- 2000ms
- 内存
- 64MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者