1 条题解
-
0
#4922. 「POI2019 R1」小机器人 Robby the little robot 题解
题目分析
我们有一个机器人在网格平面上移动,初始位置在 ,面向北。给定一个长度为 的命令序列 ,机器人会循环执行这些命令:
- 第 步:向前移动 个单位,然后右转
每个移动或转弯都消耗 秒。电池总共能支持 秒。我们需要计算在时间 到 之间,机器人经过目标点 的次数。
关键约束: 可以达到 ,因此不能直接模拟每一步。
核心观察
1. 方向周期性
机器人的方向变化是周期性的:
- 方向序列:北(0°) → 东(90°) → 南(180°) → 西(270°) → 北(360°)...
- 每 步完成一个方向循环
2. 命令周期性
由于命令序列长度为 ,完整的运动周期是 步:
- 每 步后,机器人回到初始方向,并完成 个完整的命令循环
3. 位置变化的数学表达
设第 步移动的距离为 ,方向为 。
位置 经过 步后可以表示为:
$$X_k = \sum_{i=1}^k a_i \cdot \cos(dir_i \cdot 90°) $$$$Y_k = \sum_{i=1}^k a_i \cdot \sin(dir_i \cdot 90°) $$由于方向只有 4 种,可以简化为:
- 北(+y):
- 东(+x):
- 南(-y):
- 西(-x):
算法设计
步骤1:预处理前缀和
定义四个方向的前缀和数组:
sumN[i]:前 步中向北移动的总距离sumE[i]:前 步中向东移动的总距离sumS[i]:前 步中向南移动的总距离sumW[i]:前 步中向西移动的总距离
这样,经过 步后的位置为:
步骤2:计算周期位移
计算一个完整周期( 步)的位移:
$$\Delta X = X_{4n} - X_0 = \text{sumE}[4n] - \text{sumW}[4n] $$$$\Delta Y = Y_{4n} - Y_0 = \text{sumN}[4n] - \text{sumS}[4n] $$步骤3:批量处理完整周期
设总步数为 ,计算完整周期数:
剩余步数:
对于第 个周期结束时的位置():
步骤4:检查每个时间点的位置
我们需要检查所有时间点 是否在目标点 。
对于每个完整周期内的位置:
- 基础位置:
- 相对偏移:使用前缀和数组计算 步后的位置
- 总位置:基础位置 + 相对偏移
步骤5:优化检查过程
由于 很大,我们不能检查每个时间点。优化方法:
观察:在一个周期内,每个时间点的位置是固定的。我们只需要:
- 预处理一个周期内所有可能到达目标点的时间点
- 对于每个完整周期,检查这些时间点加上周期偏移后是否在时间范围内
- 对剩余部分单独检查
具体实现
#include <bits/stdc++.h> using namespace std; using ll = long long; int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); ll n, t; cin >> n >> t; vector<ll> d(n); for (int i = 0; i < n; i++) { cin >> d[i]; } ll target_x, target_y; cin >> target_x >> target_y; // 方向:0-北, 1-东, 2-南, 3-西 vector<ll> sumN(4*n+1, 0), sumE(4*n+1, 0), sumS(4*n+1, 0), sumW(4*n+1, 0); // 计算前缀和 for (int i = 1; i <= 4*n; i++) { int cmd_idx = (i-1) % n; int dir = (i-1) % 4; sumN[i] = sumN[i-1]; sumE[i] = sumE[i-1]; sumS[i] = sumS[i-1]; sumW[i] = sumW[i-1]; if (dir == 0) sumN[i] += d[cmd_idx]; // 北 else if (dir == 1) sumE[i] += d[cmd_idx]; // 东 else if (dir == 2) sumS[i] += d[cmd_idx]; // 南 else sumW[i] += d[cmd_idx]; // 西 } // 计算周期位移 ll deltaX = sumE[4*n] - sumW[4*n]; ll deltaY = sumN[4*n] - sumS[4*n]; // 预处理一个周期内能到达目标点的时间 vector<ll> valid_times; for (ll k = 0; k <= min(t, 4*n*1LL); k++) { ll xk = sumE[k] - sumW[k]; ll yk = sumN[k] - sumS[k]; if (xk == target_x && yk == target_y) { valid_times.push_back(k); } } ll result = 0; ll max_steps = min(t, 4*n * (t / (4*n) + 2)); // 安全上限 // 检查每个完整周期 ll cycles = max_steps / (4*n); for (ll c = 0; c < cycles; c++) { ll base_x = c * deltaX; ll base_y = c * deltaY; for (ll time_in_cycle : valid_times) { ll total_time = c * 4*n + time_in_cycle; if (total_time > t) continue; ll rel_x = sumE[time_in_cycle] - sumW[time_in_cycle]; ll rel_y = sumN[time_in_cycle] - sumS[time_in_cycle]; if (base_x + rel_x == target_x && base_y + rel_y == target_y) { result++; } } } // 检查剩余部分 ll remaining_steps = max_steps % (4*n); for (ll k = cycles * 4*n; k <= min(t, cycles * 4*n + remaining_steps); k++) { ll cycle_num = k / (4*n); ll time_in_cycle = k % (4*n); ll base_x = cycle_num * deltaX; ll base_y = cycle_num * deltaY; ll rel_x = sumE[time_in_cycle] - sumW[time_in_cycle]; ll rel_y = sumN[time_in_cycle] - sumS[time_in_cycle]; if (base_x + rel_x == target_x && base_y + rel_y == target_y) { result++; } } cout << result << "\n"; return 0; }复杂度分析
- 预处理: 计算前缀和
- 周期内检查: 预处理有效时间点
- 周期处理: 最坏情况,但实际由于 很大,需要进一步优化
进一步优化
对于 的情况,需要数学推导来直接计算而不遍历所有周期。核心思想:
- 将问题转化为求解方程: 和
- 对于每个周期内的相对位置 ,解出满足条件的周期数
- 检查这些周期数是否在范围内
这样复杂度降为 ,可以处理 的情况。
总结
本题的关键在于发现机器人运动的周期性,并通过数学方法批量处理,避免直接模拟每一步。标签应为:周期性分析、模拟优化、数学推导。
- 1
信息
- ID
- 3751
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者