专栏文章
P14269 兄妹
算法·理论参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minkax6s
- 此快照首次捕获于
- 2025/12/02 03:48 3 个月前
- 此快照最后确认于
- 2025/12/02 03:48 3 个月前
题解:P14259 兄妹(siblings)
题目大意
书店有若干排书架,第 排书架包含坐标 (),出入口在 。有 本书需要放到指定位置 。两个人从 出发,各自负责一部分书,放书后返回 。移动规则:
- 在同一排内,可以上下移动。
- 在不同排之间,只能通过出入口移动。
求两人中用时较长者的最短用时。
解题思路
关键观察
-
每排的最远距离:对于同一排 ,如果有多本书,只需要考虑最远的那本,因为在同一排内移动可以顺便放其他书。设 ,如果该排没有书,则 。
-
总用时计算:如果一个人负责的集合 包含排号 ,那么他的用时为:
因为需要走到最远的排,并且每排都需要来回。
- 问题转化:设最大排号为 。我们需要将排 分成两个集合 和 ,使得:
最小。由于 是最大排号,必然有一个集合包含 ,不妨设 包含 ,则 。另一个集合 的排号均不超过 ,设 。
那么问题转化为:求
其中 ,,且 。
- 动态规划:设总距离和 。我们枚举 (即 的最大排号),但更高效的方法是使用背包 DP 计算所有可能的子集和。
设 表示前 排中能否选出一个子集,使得其 之和为 。我们遍历排号 从 到 ,更新背包。对于每个 ,我们考虑前 排的子集和 ,那么此时:
- 由前 排中的若干排组成,最大排号为 ,距离和为 。
- 包含后 排以及前 排中未选中的排,距离和为 ,最大排号为 。
则用时为 。我们取所有可能中的最小值。
算法步骤
- 预处理每排的 。
- 找到最大排号 。
- 计算总距离和 。
- 初始化背包数组 ,大小为 ,。
- 初始化答案 为一个较大值。
- 对于 从 到 :
- 如果 ,则从后往前更新背包:对于 从 到 ,如果 为真,则设置 为真。
- 然后遍历所有 从 到 ,如果 为真,则计算 ,并更新 。
- 输出 。
复杂度分析
- 时间复杂度:,其中 是最大排号, 是总距离和。由于 ,,但实际中 可能较小,因为每排的 不会都很大。
- 空间复杂度:,使用一维背包数组。
代码实现
CPP#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_R = 500;
const int MAX_SUM = 250000;
int x[MAX_R + 1];
bool f[MAX_SUM + 1];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while (T --) {
memset(x, 0, sizeof(x));
memset(f, 0, sizeof(f));
int n;
cin >> n;
int max_r = 0;
for (int i = 0; i < n; i ++) {
int r, c;
cin >> r >> c;
x[r] = max(x[r], c);
max_r = max(max_r, r);
}
int r = max_r;
while (r > 0 && x[r] == 0) r --;
int total_c = 0;
for (int i = 1; i <= r; i++) {
total_c += x[i];
}
f[0] = true;
int ans = 300000;
int s = 0;
for (int i = 1; i <= r; i ++) {
if (x[i] == 0) continue;
for (int j = s; j >= 0; j --) {
if (f[j]) {
f[j + x[i]] = true;
}
}
s += x[i];
for (int j = 0; j <= s; j ++) {
if (f[j]) {
ans = min(ans, max(i + j, r + total_c - j));
}
}
}
cout << ans * 2 << '\n';
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...