AcWing3305 -- 建图

1. 题目描述

给定我们一些初始作物,和作物之间杂交的规则(作物 a 和作物 b 杂交产生种子 c,花费作物 a 和作物 b 成熟时间的最大值),让我们求,某个作物 T 出现的最小时间



2. 思路

建图

最短路应该是可以看出来的,但是对于给定的规则,作物 a 和作物 b 杂交产生种子 c,花费作物 a 和作物 b 成熟时间的最大值,我们如何根据这条规则建立边呢?
其实这也没什么好解释的,下面直接告诉你答案:

a -> c,边权为 b
b -> c,边权为 a

值得注意的是,这里的边权并不是传统意义上的边权,它代指一个条件。
bc 的边权为 a 表示:b 只有满足拥有 a 的条件,才能到达(与 a 杂交产生) c。同理,
ac 的边权为 b 表示,a 只有满足拥有 b 的条件,才能到达(与 b 杂交产生)c
而真正的边权,就是 max(cost[a], cost[b]),这里的 cost[a] 就是题目给定的作物 a 成熟的时间。
所以说,通过本题,你应该明白如下结论:

边不止可以存储边权,还可以存储其他前驱结点以及其他的信息

抽象出问题的模型并建图往往是最难的!

求解

好了,现在我们已经建立了一个图,接下来该如下求解呢?
题目给定了我们一些初始作物,要求我们求目标作物,经典的 N -> 1 问题啊!你应该立马想到,将 N 抽象为 1(通过虚拟源点),然后就是求单源最短路!
当然,我们也可以不添加虚拟源点,而是模拟虚拟源点的第一趟遍历,将所有初始时给定的作物都加入队列中即可
由于边权都是正值,因此 dijkstraspfa 都是可行的。

下面代码中 (1) 的解释

int new_dist = max(distance, dist[w[i]]) + max(cost[ver], cost[w[i]]);
对于这句代码,max(cost[ver], cost[w[i]]) 可能还容易理解,因为我们建图的时候就是如此设置的。
但是 max(distance, dist[w[i]]) 可能不容易理解了,其实很简单,虽然我们可以通过作物 a 和 作物 b 杂交产生作物 c
但我们也得保证 ab 先存在啊,而 ab 最晚的那个出现的时间起,我们才可能杂交,否则少一个怎么杂交。。



3. 代码(dijkstra)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

typedef pair<int, int> PII;

// 添加虚拟源点要多加边数
const int N = 2010, M = 3e5 + 10;

int n, m, k, T;
int cost[N];
int h[N], e[M], ne[M], w[M], idx;
int dist[N];
bool st[N];

void add(int a, int b, int c)
{
    w[idx] = c;
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dijkstra()
{
    priority_queue<PII, vector<PII>, greater<PII>> q;
    memset(dist, 0x3f, sizeof dist);
    dist[0] = 0;
    q.push({0, 0}); // push virtual source node
    while(q.size())
    {
        auto [distance, ver] = q.top(); q.pop();
        if(st[ver]) continue;
        st[ver] = true;
        for(int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            int new_dist = max(distance, dist[w[i]]) + max(cost[ver], cost[w[i]]);  // (1)
            if(new_dist < dist[j])
            {
                dist[j] = new_dist;
                q.push({new_dist, j});
            }
        }
    }
}

int main()
{
    memset(h, -1, sizeof h);
    cin >> n >> m >> k >> T;
    for(int i = 1; i <= n; i ++ )   cin >> cost[i];
    // 作物最小编号为0,因为使用0作为虚拟源点
    for(int i = 0; i < m; i ++ )
    {
        int x;  cin >> x;
        add(0, x, 0);   // “边权”也是0
    }
    for(int i = 0; i < k; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, c, b);
        add(b, c, a);
    }
    dijkstra();
    cout << dist[T] << endl;
    return 0;
}

4. 参考

ref - spfa
ref2 - dijkstra

posted @   光風霽月  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示