题解 [CEOI2005] Depot Rearrangement
[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
思路分析
不难发现,对于每一部分的集装箱,同种类只出现过一次的集装箱我们是不会动它的,我们会移动的是同种类出现次数大于
设
考虑构造二分图,左边点集为
若
若
对样例建立二分图
通过这样的方法构造出的二分图,其中一条从左到右再到左的路径为
每当我们在图中走完一条回路时,
可以证明图中每个子图一定存在欧拉回路。因为我们建边是根据
具体的构造方案为,对于每一个极大联通子图任选一个点当起点,求欧拉回路。
记录经过的边的编号序列,倒序遍历序列,每遇到
为什么要倒序枚举序列?每次的移动相当于在图中经过
对于一个具有
空间只有
代码实现
#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;
}