UVa 10076 The Bumpy Robot

Bumpy Robot路径优化

题目描述

颠簸机器人在一个 M×NM \times NM×N 的网格上移动,每个格子有一个整数高度。从高度 h1h_1h1 的格子移动到相邻高度 h2h_2h2 的格子需要消耗一定的能量和时间,公式如下:

能量消耗:

ΔE={⌈α1(h1−h2)⌉+γ,若 h1>h2γ,若 h1=h2⌈α2(h2−h1)⌉+γ,若 h1<h2 \Delta E = \begin{cases} \lceil \alpha_1 (h_1 - h_2) \rceil + \gamma, & \text{若 } h_1 > h_2 \\ \gamma, & \text{若 } h_1 = h_2 \\ \lceil \alpha_2 (h_2 - h_1) \rceil + \gamma, & \text{若 } h_1 < h_2 \end{cases} ΔE=α1(h1h2)⌉+γ,γ,α2(h2h1)⌉+γ, h1>h2 h1=h2 h1<h2

时间消耗(原题目描述中第三个式子中是 γ\gammaγ,实际应为 δ\deltaδ):

ΔT={⌈β1(h1−h2)⌉+δ,若 h1>h2δ,若 h1=h2⌈β2(h2−h1)⌉+δ,若 h1<h2 \Delta T = \begin{cases} \lceil \beta_1 (h_1 - h_2) \rceil + \delta, & \text{若 } h_1 > h_2 \\ \delta, & \text{若 } h_1 = h_2 \\ \lceil \beta_2 (h_2 - h_1) \rceil + \delta, & \text{若 } h_1 < h_2 \end{cases} ΔT=β1(h1h2)⌉+δ,δ,β2(h2h1)⌉+δ, h1>h2 h1=h2 h1<h2

其中 α1,α2,β1,β2\alpha_1, \alpha_2, \beta_1, \beta_2α1,α2,β1,β2 是正浮点数,γ,δ\gamma, \deltaγ,δ 是正整数。

机器人需要从起点 (rs,cs)(r_s, c_s)(rs,cs) 移动到终点 (rt,ct)(r_t, c_t)(rt,ct) ,在总能量消耗不超过 EEE 的前提下,最小化总时间。如果无法到达,则输出 failed

输入格式
多组测试数据,每组第一行是 M,NM, NM,N1≤M,N≤151 \leq M,N \leq 151M,N15 ),接下来两行分别是参数 α1,α2,γ\alpha_1, \alpha_2, \gammaα1,α2,γβ1,β2,δ\beta_1, \beta_2, \deltaβ1,β2,δ 。然后是 MMM 行每行 NNN 个整数表示高度。最后一行是 rs,cs,rt,ct,Er_s, c_s, r_t, c_t, Ers,cs,rt,ct,E1≤E≤2001 \leq E \leq 2001E200 )。输入以 0 0 结束。

输出格式
每组输出一行,若可达输出最小时间,否则输出 failed


题目分析

这是一个 带约束的最短路问题 。我们需要在 能量限制 下找到 时间最短 的路径。

关键点

  • 网格规模很小:最多 15×15=22515 \times 15 = 22515×15=225 个格子。
  • 能量上限 E≤200E \leq 200E200 ,可以作为状态的一部分。
  • 目标是 最小化时间 ,能量是 约束条件

思路
我们可以把状态定义为 (r,c,e)(r, c, e)(r,c,e) ,表示到达格子 (r,c)(r, c)(r,c) 且已消耗能量 eee
dp[r][c][e]dp[r][c][e]dp[r][c][e] 记录到达该状态的最小时间。
由于时间是我们要最小化的目标,我们可以使用 优先队列(Dijkstra\texttt{Dijkstra}Dijkstra 按时间递增的顺序扩展状态,确保第一次扩展到终点时的时间就是最优解。

状态转移
(r,c,e)(r, c, e)(r,c,e) 可以向上、下、左、右四个方向移动,计算新格子 (nr,nc)(nr, nc)(nr,nc) 对应的能量增量 Δe\Delta eΔe 和时间增量 Δt\Delta tΔt
如果 e+Δe≤Ee + \Delta e \leq Ee+ΔeE 且新时间更优,则更新 dp[nr][nc][e+Δe]dp[nr][nc][e+\Delta e]dp[nr][nc][e+Δe] 并入队。

终止条件
当优先队列为空时,检查所有 dp[rt][ct][e]dp[rt][ct][e]dp[rt][ct][e]e=0..Ee = 0..Ee=0..E )的最小值,即为答案。

复杂度分析
状态总数: O(M×N×E)≈225×201≈45225O(M \times N \times E) \approx 225 \times 201 \approx 45225O(M×N×E)225×20145225
每个状态扩展 444 个方向,总操作约 181818 万次,完全可行。


解题思路详细步骤

  1. 读入数据:注意浮点数参数和高度矩阵。
  2. 状态定义minTime[r][c][e]minTime[r][c][e]minTime[r][c][e] 表示到达 (r,c)(r,c)(r,c) 且消耗能量 eee 的最小时间,初始化为无穷大。
  3. 起点初始化minTime[rs][cs][0]=0minTime[r_s][c_s][0] = 0minTime[rs][cs][0]=0 ,将 (rs,cs,0,0)(r_s, c_s, 0, 0)(rs,cs,0,0) 加入优先队列。
  4. Dijkstra\texttt{Dijkstra}Dijkstra 扩展
    • 每次取出时间最小的状态 (r,c,e,t)(r, c, e, t)(r,c,e,t)
    • 如果 t>minTime[r][c][e]t > minTime[r][c][e]t>minTime[r][c][e] ,说明已过期,跳过。
    • 向四个方向移动,计算 Δe\Delta eΔeΔt\Delta tΔt ,注意使用 ceil 取整。
    • e+Δe≤Ee + \Delta e \leq Ee+ΔeEt+Δt<minTime[nr][nc][e+Δe]t + \Delta t < minTime[nr][nc][e+\Delta e]t+Δt<minTime[nr][nc][e+Δe] ,则更新并入队。
  5. 收集答案:在扩展过程中,如果到达终点 (rt,ct)(r_t, c_t)(rt,ct) ,可以记录当前时间。最终答案为所有到达终点的状态中时间的最小值。
  6. 输出:若答案仍为无穷大则输出 failed ,否则输出最小时间。

特殊处理
如果起点等于终点,直接输出 000


代码实现

// The Bumpy Robot
// UVa ID: 10076
// Verdict: Accepted
// Submission Date: 2025-12-06
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

struct State {
    int row, col, energy;
    int time;
    State(int r, int c, int e, int t) : row(r), col(c), energy(e), time(t) {}
    // 优先队列按时间从小到大排序
    bool operator<(const State& other) const {
        return time > other.time;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    const int INF = 1e9;
    const int dr[4] = {-1, 1, 0, 0};
    const int dc[4] = {0, 0, -1, 1};
    
    int M, N;
    while (cin >> M >> N, M || N) {
        double alpha1, alpha2, beta1, beta2;
        int gamma, delta;
        cin >> alpha1 >> alpha2 >> gamma;
        cin >> beta1 >> beta2 >> delta;
        
        vector<vector<int>> height(M, vector<int>(N));
        for (int i = 0; i < M; ++i)
            for (int j = 0; j < N; ++j)
                cin >> height[i][j];
        
        int rs, cs, rt, ct, E;
        cin >> rs >> cs >> rt >> ct >> E;
        rs--; cs--; rt--; ct--; // 转为0-based索引
        
        // minTime[r][c][e] = 到达(r,c)且消耗能量e的最小时间
        vector<vector<vector<int>>> minTime(M, vector<vector<int>>(N, vector<int>(E + 1, INF)));
        priority_queue<State> pq;
        
        if (rs == rt && cs == ct) {
            cout << "0\n";
            continue;
        }
        
        minTime[rs][cs][0] = 0;
        pq.emplace(rs, cs, 0, 0);
        
        int ans = INF;
        
        while (!pq.empty()) {
            State cur = pq.top(); pq.pop();
            int r = cur.row, c = cur.col, e = cur.energy, t = cur.time;
            
            if (t > minTime[r][c][e]) continue; // 已有更优状态
            
            // 尝试四个方向
            for (int dir = 0; dir < 4; ++dir) {
                int nr = r + dr[dir], nc = c + dc[dir];
                if (nr < 0 || nr >= M || nc < 0 || nc >= N) continue;
                
                int h1 = height[r][c], h2 = height[nr][nc];
                int deltaE, deltaT;
                
                // 计算能量增量
                if (h1 > h2) {
                    deltaE = ceil(alpha1 * (h1 - h2)) + gamma;
                    deltaT = ceil(beta1 * (h1 - h2)) + delta;
                } else if (h1 == h2) {
                    deltaE = gamma;
                    deltaT = delta;
                } else {
                    deltaE = ceil(alpha2 * (h2 - h1)) + gamma;
                    deltaT = ceil(beta2 * (h2 - h1)) + gamma; // 注意原文中时间公式第三项为 gamma,实际应为 delta
                }
                
                int ne = e + deltaE;
                int nt = t + deltaT;
                
                if (ne > E) continue; // 能量超限
                if (nt >= minTime[nr][nc][ne]) continue; // 已有更优
                
                minTime[nr][nc][ne] = nt;
                pq.emplace(nr, nc, ne, nt);
                
                if (nr == rt && nc == ct) {
                    ans = min(ans, nt);
                }
            }
        }
        
        if (ans == INF) cout << "failed\n";
        else cout << ans << "\n";
    }
    
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值