T6. 燃料

小明要开车从家里前往远方的城市 C,他家到城市的路程是 L 公里。他的车每开 1 公里需要消耗 1 升汽油,车的油箱最多只能装 V 升汽油,出发前他的车是加满汽油的。
路途中有 n 个加油站,第 i 个加油站离小明家的路程是 xi 公里,在这里可以花费 wi 元加 vi 升汽油且只能加一次。但是如果超过了油箱容量,就只能加满为止。
问小明到达城市 C 至少需要花费多少?

输出到达城市 C 需要花费的最小值。如果无法到达,输出 -1

限制:

  • 1n2000
  • 1V2000
  • 1L109
  • 0x1x2xnL
  • 0vi109
  • 0wi109

算法分析

dp[i][j] 表示到第 i 个加油站且(加油前)油剩 j 升时的最小花费
初值:dp[0][V]=0,其他 dp=
用求好的 dp[i][j]`` 去推出 dp[i+1][?]`` 的最小
0 个加油站看做是家 x0=0
n+1 个加油站看做是终点 xn+1=L

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
const ll INF = 1e18;
int x[2005], v[2005], w[2005];
ll dp[2005][2005];
inline void chmin(ll& x, ll y) { if (x > y) x = y; }
int main() {
int n, V, L;
cin >> n >> V >> L;
for (int i = 1; i <= n; ++i) {
cin >> x[i] >> v[i] >> w[i];
}
x[n+1] = L;
memset(dp, 0x3f, sizeof dp);
dp[0][V] = 0;
rep(i, n+1) {
rep(j, V+1) {
int t = x[i+1]-x[i];
int nj;
// 不加油直接出发,到下一站要消耗t
nj = j-t;
if (nj >= 0) chmin(dp[i+1][nj], dp[i][j]);
// 加油后出发,到下一站剩余 min(j+v[i], V)-t
nj = min(j+v[i], V)-t;
if (nj >= 0) {
chmin(dp[i+1][nj], dp[i][j]+w[i]);
}
}
}
ll ans = INF;
rep(j, V+1) chmin(ans, dp[n+1][j]);
if (ans == INF) ans = -1;
cout << ans << '\n';
return 0;
}

T7. 穿越战场

有一个长方形战场,战场地图被分成 nm 列一共 n×m 个方格区域。从北向南编号为 1n 行,从西向东编号为 1m 列。
战场上有若干个格子内有敌人的哨所,第 i 个哨所在第 xi 行第 yi 列。
你现在要和东北方的大部队会和。你的任务是从战场南边界(第 n 行)的任意位置进入战场,在 T 分钟内,从北边界(第 1 行)的一个尽量靠东的位置离开战场。战场是一个平原,每次移动,你都可以从当前所在格子移动到上下左右的格子,进入战场的时刻定为第 1 分钟,1 次移动花费 1 分钟的时间。只要到达北边界的时间小于等于 T 分钟就可以。
移动过程中,离敌人的哨所越近,被发现的风险越高。我们定义整个移动路线的安全程度,等于途中离敌人哨所距离的最小值。我们定义距离为行列差的绝对值之和:x1y1 列与 x2y2 列的距离为 |x1x2|+|y1y2|
在满足 T 分钟内离开战场的前提下,求你的路线的安全程度的最大值,安全程度最大的路线的终点的列坐标。如果有多条路线安全程度都是最大,输出其中列坐标最大的终点。

限制:

  • 2n,m1000
  • nTn×m
  • 1xin
  • 1yim

算法分析

二分安全度 d
check(d) 有没有安全度 d (也就是路线上每格与哨所距离 d)且时间 T 的路线
路线上每格与哨所距离 d,我们可以不走与哨所距离 <d 的格子,判断能否在 T 的时间内到达北边界
以南边界上所有点为起点,做一遍多源bfs,求出所有北边界上点的最短路,之后找一下北边界上有无最短路 T 的格子,返回其中最大的列坐标,没找到返回 0
在 check 中要判断每个格子与哨所距离是否 <d,如果在 check 中现场判断的话:O(n2m2)
只能在二分前,先计算出每个点与哨所的最短距离(最短路)
以所有哨所为起点,做一次多源bfs

最后的时间复杂度为 O(log(n+m)(n+m))