酷代码 AI
菜单
服务商

Farmer John 有 NN 只奶牛(4 \leq N \leq 124≤N≤12,NN 是偶数)。他们建立了一套原生的系统,使得奶牛与他的朋友可以通过由干草保护的线路来进行对话交流。 每一头奶牛在这个牧场中正好有 33 个朋友,并且他们必须把自己安排在一排干草堆中。 一条长 LL 的线路要占用刚好 LL 堆干草来保护线路。比如说,如果有两头奶牛分别在草堆 44 与草堆 77 中,并且他们是朋友关系,那么我们就需要用 33 堆干草来建造线路,使他们之间能够联系。 假设每一对作为朋友的奶牛都必须用一条单独的线路来连接,并且我们可以随便地改变奶牛的位置,请计算出我们建造线路所需要的最少的干草堆。 输入格式 第 11 行:一个整数 NN。为了方便,我们给奶牛用 1\sim N1∼N 的数字进行编号。 第 2, 3, \dots, N + 12,3,…,N+1 行:每一行都有三个在 1\sim N1∼N 中的整数。第 i+1i+1 行的数字代表着第 ii 头奶牛的三个朋友的编号。显然,如果奶牛 ii 是奶牛 jj 的三个朋友之一,那么奶牛 jj 也是奶牛 ii 的三个朋友之一。

我们先把问题翻译成更清晰的模型。 - 有 \( N \) 头奶牛(\( N \) 是偶数,且 \( 4 \leq N \leq 12 \))。 - 每头奶牛有恰好 3 个朋友,朋友关系是双向的。 - 奶牛要排成一排(即 1 到 \( N \) 的某个排列)。 - 如果两头奶牛是朋友,它们之间必须用一条线路连接,线路长度等于它们在排列中的位置差的绝对值。 - 线路的“干草堆数”等于长度。 - 目标:安排奶牛的位置,使所有朋友关系的线路长度之和最小。 --- ## 1. 问题重述 输入是 \( N \) 和 \( N \) 行,每行 3 个整数,表示该编号奶牛的朋友。 输出是最小总线路长度。 因为朋友关系是双向的,所以整个关系构成一个 3-正则图(每个度数为 3)。 总边数 \( E = \frac{N \times 3}{2} \),必须是整数,所以 \( N \) 是偶数满足条件。 --- ## 2. 思路 这是一个最小化排列总边长的问题。 已知图 \( G = (V, E) \),\( |V| = N \),\( |E| = 3N/2 \)。 我们要找一个排列 \( p: V \to \{1, 2, \dots, N\} \),使得 \[ \sum_{(u,v) \in E} |p(u) - p(v)| \] 最小。 --- ## 3. 等价问题 在排列中,边的长度 = 位置差。 对于固定的排列,总长度就是每条边两端点位置差的绝对值之和。 --- ## 4. 已知性质 这是一个最小线性排列问题(Minimum Linear Arrangement, MinLA),已知是 NP-hard 的,但 \( N \leq 12 \) 很小,可以暴力搜索所有排列 \( N! \) 种。 \( 12! = 479001600 \),直接全排列枚举在普通计算机上会慢(约几分钟),但可以优化剪枝或状态压缩。 --- ## 5. 优化 - 因为图是 3-正则,对称性:排列正反是一样的(总长度不变),可以只考虑 \( p(1) < p(N) \) 的情况,减少一半搜索。 - 可以提前剪枝:在构造排列的过程中,部分计算当前已确定位置的点之间的边贡献,如果已经超过已知的最小值,则剪枝。 - 用 DFS 生成排列,同时累加当前总长度,一旦超过当前最优解就回溯。 --- ## 6. 直接算法(DFS + 剪枝) 我们用一个数组 `pos[1..N]` 表示奶牛的位置(排列的逆:`pos[i]` 是奶牛 i 的位置),或者用 `order[1..N]` 表示位置上的奶牛编号。 用邻接表存图 `adj`。 DFS 过程: - 当前在决定 `order[k]` 放哪头奶牛。 - 当确定了一头奶牛在位置 k 时,我们计算它和已经确定位置的朋友之间的边长度,加到当前总长度 `cur_sum` 中。 - 如果 `cur_sum` 已经大于等于已知 `min_sum`,剪枝。 - 搜索完所有排列后,`min_sum` 就是答案。 --- ## 7. 实现细节 我们可以用 `used[1..N]` 标记奶牛是否已被放置。 `order[1..N]` 记录位置上的奶牛编号。 `cur_sum` 是当前部分排列下已确定的边的长度和。 注意:每条边只计算一次,可以在编号小的奶牛确定位置时加入计算,避免重复。 但更简单的方法是:当两个端点都确定位置时,才计算该边长度。但这样剪枝效果差。 更好的方法是:当放置一个奶牛时,对于它的每个朋友,如果朋友已经放置,则 `cur_sum += abs(current_pos - friend_pos)`。 这样每条边在第二个端点放置时被加入,不会重复。 --- ## 8. 复杂度 最坏 \( O(N!) \),但剪枝后对于 \( N=12 \) 能在可接受时间运行。 --- ## 9. 示例代码(C++ 思路) ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int N; vector<int> adj[13]; int order[13]; // order[pos] = cow_id int pos[13]; // pos[cow_id] = position (0 if not placed) int min_total = 1e9; void dfs(int k, int cur_sum) { if (cur_sum >= min_total) return; if (k > N) { min_total = cur_sum; return; } // 尝试将某头未放置的奶牛放在位置 k for (int cow = 1; cow <= N; cow++) { if (pos[cow] != 0) continue; pos[cow] = k; order[k] = cow; int add = 0; for (int friend_id : adj[cow]) { if (pos[friend_id] != 0) { add += abs(k - pos[friend_id]); } } dfs(k + 1, cur_sum + add); pos[cow] = 0; } } int main() { cin >> N; for (int i = 1; i <= N; i++) { int a, b, c; cin >> a >> b >> c; adj[i].push_back(a); adj[i].push_back(b); adj[i].push_back(c); // 朋友关系是双向的,但输入中每个朋友关系会出现两次,所以图结构正确 } dfs(1, 0); cout << min_total << endl; return 0; } ``` [2025-11-14 21:49:41 | AI问答 | 1306点数解答]

相关提问
发财导航,免费问AI
实用工具查看更多