3651: 带传送的最小路径成本
如果没有传送,本题就是leetcode 64:最小路径和。注意本题不计入起点的值。
在有传送的情况下,可以用一个额外的维度表示传送次数。定义 f[t][i+1][j+1] 表示++在恰好使用 t 次传送的情况下,从左上角 (0,0) 到 (i,j) 的最小总成本。++

传送条件只依赖于格子的数值,而不依赖于位置。
min_f [x] 表示++格子值为 x++的最小状态值(如果不存在则为 ∞)。【从左上角 (0,0) 到任一格子值为x中的最小总成本】
suf_min_f [x] 表示格子值 >=x 的最小状态值。计算方法:倒序遍历min_f,一层一层更新格子值 >=x 的最小状态值。(最大格子值为mx)
//更新后缀最小值
for(int i=mx;i>=0;i--){
suf_min_f[i]=min(suf_min_f[i+1],min_f[i]);
}
答案为 f[k][m][n]。虽然题目要求使用「至多」k 次传送,但由于我们可以原地传送,所以传送的次数越多,总成本是不会增大的。所以「至多」k 次传送等于「恰好」k 次传送。
代码实现时,f 数组的前两个维度可以优化掉。
class Solution {
public:
int minCost(vector<vector<int>>& grid, int k) {
int n=grid[0].size();
int mx=0;
for(auto& row:grid){
mx=max(mx,ranges::max(row)); //找到最大的格子值x
}
vector<int> suf_min_f(mx+2,INT_MAX);
vector<int> min_f(mx+1);
vector<int> f(n+1);
for(int t=0;t<=k;t++){
ranges::fill(min_f,INT_MAX);
//最小路径和(空间优化写法)
ranges::fill(f,INT_MAX/2);
f[1]=-grid[0][0]; //起点的成本不算
for(auto& row:grid){
for(int j=0;j<n;j++){
int x=row[j];
f[j+1]=min(min(f[j],f[j+1])+x,suf_min_f[x]);
min_f[x]=min(min_f[x],f[j+1]);
}
}
//倒序计算后缀最小值,供下一轮t+1使用
for(int i=mx;i>=0;i--){
suf_min_f[i]=min(suf_min_f[i+1],min_f[i]);
}
}
return f[n];
}
};