题目
思路
- 对n个结点的松果个数排序, 二分最大松果个数
check(x)
, 跑最短路, 在不访问比x松果个数多的节点的情况下,从起点到终点消耗的最小体力
代码
Code
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
const int N = 1e4 + 10, INF = 0x3f3f3f3f;
int a[N]; // 节点i的值为 a[i]
int b[N]; // 排第i小的节点为 节点b[i]
int idx[N]; // 节点i排 第idx[i] 小
int n, m, st, ed, h;
int dis[N]; // 距起点最短距离
int vis[N]; // 是否已确定最短路
struct Edge
{
int v, w;
};
vector<Edge> adj[N];
// 堆优化dijkstra最短路
// 检查在仅访问松果个数按升序排序后 第1~x个结点的条件下,
// 从起点到终点消耗最小体力是否 <=h
bool check(int x)
{
// 注意初始化
memset(dis, 0x3f, sizeof dis);
memset(vis, 0, sizeof vis);
priority_queue<PII, vector<PII>, greater<PII>> q;
dis[st] = 0; q.push({0, st});
while (q.size())
{
int u = q.top().second; q.pop();
if (vis[u]) continue;
vis[u] = true;
for (auto [v, w]: adj[u])
{
if (idx[v] > x) continue; // 排除 x 之后的结点
int d = dis[u] + w;
if (d < dis[v])
{
dis[v] = d;
q.push({d, v});
}
}
}
return dis[ed] <= h; // 体力足够回家
}
void solv()
{
cin >> n >> m >> st >> ed >> h;
for (int i = 1; i <= n; i ++)
{
cin >> a[i];
b[i] = i;
}
while (m --)
{
int u, v, w;
cin >> u >> v >> w;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}
sort(b+1, b+1+n, [&](int x, int y) {return a[x] < a[y];});
for (int i = 1; i <= n; i ++) idx[b[i]] = i;
int l = 1, r = n; // b[l ~ r]
// 二分最大最小值
while (l < r)
{
int mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid + 1;
}
// 注意检查无解情况
if (check(l)) cout << a[b[l]] << endl;
else cout << -1 << endl;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T = 1;
// cin >> T;
while (T --)
{
solv();
}
return 0;
}
总结
- 二分答案, 往往用于求如此的最大最小值.