newcoder61132D <最短路 二分答案>

发布时间 2023-07-12 19:23:05作者: O2iginal

题目

松鼠回家

思路

  • 对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;
}

总结

  1. 二分答案, 往往用于求如此的最大最小值.