当前位置: 首页 > article >正文

图论——单源最短路的综合应用

acwing1135.新年好

本题要求的不是单源点最短路,而是从起点开始,走过 a , b , c , d , e a,b,c,d,e a,b,c,d,e五个点的最短距离。

考虑枚举遍历从1开始走遍 a , b , c , d , e a,b,c,d,e a,b,c,d,e点的顺序,方案数为 5 ! 5! 5!,每一个方案都要走过五条路径,例如方案 1 − b − a − e − d − c 1-b-a-e-d-c 1baedc要走过路径 ( 1 , b ) , ( b , a ) , ( a , e ) , ( e , d ) , ( d , c ) (1,b),(b,a),(a,e),(e,d),(d,c) (1,b),(b,a),(a,e),(e,d),(d,c)。考虑对于每一个方案,都做六遍dijkstra,每次求出来从 1 , a , b , c , d , e 1,a,b,c,d,e 1,a,b,c,d,e中一个点到其他点的最短距离,对于该方案所求的结果就是5个点对之间最短路径的和,这样时间复杂度就是 O ( 6 ! m l o g m ) O(6!mlogm) O(6!mlogm)。考虑先求出从这六个点出发到其他点的最短路径,在枚举所有方案,这样就起到了一个预处理的左右,时间复杂度为 O ( 6 m l o g m + 5 ! ) O(6mlogm+5!) O(6mlogm+5!)

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int,int> PII;
const int N = 50010, M = 1e5 * 2 + 10;
int dist[10][N];
bool st[N];
int h[N], e[M], ne[M], w[M];
int tot;
int source[10];
int n, m;
void add(int a, int b, int c)
{
    e[tot] = b, ne[tot] = h[a], w[tot] = c, h[a] = tot ++ ;
}
void dijkstra(int start, int dist[])
{
    memset(dist, 0x3f, N * 4);
    memset(st, 0, sizeof st);
    dist[start] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({dist[start], start});
    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();
        int ver = t.second, distance = t.first;
        if (st[ver]) continue;
        st[ver] = 1;
        for (int i = h[ver]; ~i; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i];
                heap.push({dist[j], j});
            }
        }
    }
    return ;
}
int dfs(int u, int last, int val)
{
    if (u == 6) return val;
    int res = 0x3f3f3f3f;
    for (int i = 2; i <= 6; i ++ )
    {
        int j = source[i];
        if (st[i]) continue;
        st[i] = 1;
        res = min(res, dfs(u + 1, i, val + dist[last][j]));
        st[i] = 0;
    }
    return res;
}
int main()
{
    cin >> n >> m;
    for (int i = 2; i <= 6; i ++ )
        cin >> source[i];
    source[1] = 1;
    memset(h, -1, sizeof h);
    for (int i = 1; i <= m; i ++ )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c), add(b, a, c);
    }
    for (int i = 1; i <= 6; i ++ )
        dijkstra(source[i], dist[i]);
    memset(st, 0, sizeof st);
    printf("%d\n", dfs(1, 1, 0));
    return 0;
}

acwing340.通信线路

从1号节点到n号节点之间有若干条路径,本题是要在所有路径里找第 k + 1 k+1 k+1大电缆的最小值,因此可以考虑使用二分来做。
可以用二分的情况是所求的答案在某个分界点,具有某个性质,在这个分界点的一边满足这个性质,另一边不满足。

1.对于答案x,需要满足的性质就是从1走到n需要经过大于x的边数小于等于k,作为分界点,此时大于x的边数应该等于k。
2.对于答案右边的区间,x变大了,大于x的边数就会减小,即大于x的边数小于k,同样满足性质;
2.对于答案左边的区间,x变小了,大于x的边数就会增加,即大于x的边数大于k,不满足性质。

接下来需要判断答案x是否满足上面性质。我们把边权大于x的边权置为1,小于等于x的边权置为0,这样一来图就是一个只有0,1边权的图,我们可以求出一条从1到n的最短路径,如果路径长度 ≤ k \leq k k,则说明x可行。

答案区间为 [ 0 , 1 e 6 + 1 ] [0,1e6+1] [0,1e6+1],取到0则说明存在长度 ≤ k \leq k k的路径,取到 1 e 6 + 1 1e6+1 1e6+1则说明不存在通路。

#include <iostream>
#include <cstring>
#include <deque>
using namespace std;
const int N = 1010, M = 200010;
int h[N], e[M], ne[M], w[M];
int tot;
int dist[N];
bool st[N];
int n, p, k;
void add (int a, int b, int c)
{
    e[tot] = b, ne[tot] = h[a], w[tot] = c, h[a] = tot ++ ;
}
bool check(int x)
{
    memset(dist, 0x3f, sizeof dist);
    memset(st, 0, sizeof st);
    dist[1] = 0;
    deque<int> q;
    q.push_back(1);
    while (q.size())
    {
        int t = q.front();
        q.pop_front();
        if (st[t]) continue;
        st[t] = 1;
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i], val = w[i] > x;
            if (dist[j] > dist[t] + val)
            {
                dist[j] = dist[t] + val;
                if (!val) q.push_front(j);
                else q.push_back(j);
            }
        }
    }
    return dist[n] <= k;
}
int main()
{
    cin >> n >> p >> k;   
    memset(h, -1, sizeof h);
    while (p -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add (a, b, c), add(b, a, c);
    }

    int l = 0, r = 1e6 + 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    if (r == 1e6 + 1) r = -1;
    cout << r;
    return 0;
}

acwing342.道路与航线

本题中存在两种路线:
一种是道路,双向,边权非负。
一种是航线,单向,边权可正可负。同时保证保证如果有一条航线可以从 A i A_i Ai B i B_i Bi,那么保证不可能通过一些道路和航线从 B i B_i Bi回到 A i A_i Ai
因为含有负边权,不能使用dijkstra,同时一般的spfa又会被卡,考虑把本题抽象为以下模型:
在这里插入图片描述
每一个大圆为一个连通块,大圆内的小圆(城镇)之间可以通过道路相互到达,其中深色小圆为起点,一个大圆可以通过单向的航线到达另一个大圆,大圆之间存在拓扑序。
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
typedef pair<int,int> PII;
const int N = 25010, M = 1500010;
int id[N];
int h[N], e[M], ne[M], w[M], tot;
vector<int> block[N];
queue<int> q;
int dist[N];
bool st[N];
int pid;
int din[N];
int n, r, p, s;
void add(int a, int b, int c)
{
    e[tot] = b, ne[tot] = h[a], w[tot] = c, h[a] = tot ++ ;
}
void dfs(int u, int pid)
{
    id[u] = pid;
    block[pid].push_back(u);
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (id[j]) continue;
        dfs(j, pid);
    }
    return ;
}
void dijkstra(int x)
{
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    for (auto i : block[x])
        heap.push({dist[i], i});
    while (heap.size())
    {
        auto t = heap.top();
        int ver = t.second, distance = t.first;
        heap.pop();
        if (st[ver]) continue;
        st[ver] = 1;
        for (int i = h[ver]; ~i; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i];
                if (id[j] == id[ver]) heap.push({dist[j], j});
            }
            if (id[j] != id[ver] && (-- din[id[j]]) == 0) q.push(id[j]);
        }
    }
    return ; 
}
void topsort()
{
    memset(dist, 0x3f, sizeof dist);
    dist[s] = 0;
    for (int i = 1; i <= pid; i ++ )
        if (din[i] == 0) q.push(i);
    while (q.size())
    {
        int t = q.front();
        dijkstra(t);
        q.pop();
    }
    return ;
}
int main()
{
    cin >> n >> r >> p >> s;
    memset(h, -1, sizeof h);
    while (r -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c);
    }
    for (int i = 1; i <= n; i ++ )
    {
        if (id[i]) continue;
        dfs(i, ++ pid);
    }
    while (p -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
        din[id[b]] ++ ;
    }
    topsort();
    for (int i = 1; i <= n; i ++ )
    {
        if (dist[i] > 0x3f3f3f3f / 2) puts("NO PATH");
        else cout << dist[i] << endl;
    }
    return 0;
}

acwing341.最优贸易

先求出:

从1走到 i的过程中,买入水晶球的最低价格 d m i n [ i ] dmin[i] dmin[i]
从 i走到n的过程中,卖出水晶球的最高价格 d m a x [ i ] dmax[i] dmax[i]
然后枚举每个城市作为买卖的中间城市,求出 dmax[i] - dmin[i] 的最大值即可。
d m i n [ i ] dmin[i] dmin[i] d m a x [ i ] dmax[i] dmax[i] 时,由于不是拓扑图,状态的更新可能存在环,因此不能使用动态规划,只能使用求最短路的方式。
另外,我们考虑能否使用 dijkstra 算法,如果当前 dmin[i] 最小的点是 5,那么有可能存在边 5 − > 6 , 6 − > 7 , 7 − > 5 5-> 6, 6-> 7, 7-> 5 5>6,6>7,7>5,假设当前 d m i n [ 5 ] = 10 dmin[5] = 10 dmin[5]=10,则有可能存在 6 的价格是11, 但 7 的价格是3,那么 d m i n [ 5 ] dmin[5] dmin[5] 的值就应该被更新成3,因此当前最小值也不一定是最终最小值,所以dijkstra算法并不适用,我们只能采用 spfa 算法。

#include <iostream>
#include <cstring>
using namespace std;
const int N = 100010, M = 2000010;
int h[N], rh[N], e[M], ne[M];
int val[N];
int tot;
int q[N];
int dist[N];
bool st[N];
int n, m;
int dmin[N], dmax[N];
void add(int h[], int a, int b)
{
    e[tot] = b, ne[tot] = h[a], h[a] = tot ++ ;
}
void spfa(int d[], int start, int h[], bool flag)
{
    memset(st, 0, sizeof st);
    if (!flag) memset(d, 0x3f, sizeof dmin);
    d[start] = val[start];
    int hh = 0, tt = 1;
    q[0] = start;
    st[start] = 1;
    while (hh != tt)
    {
        int t = q[hh ++ ];
        if (hh == N) hh = 0;
        st[t] = 0;
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if ((!flag && d[j] > min(d[t], val[j])) || (flag && d[j] < max(d[t], val[j])))
            {
                if (!flag) d[j] = min(d[t], val[j]);
                else d[j] = max(d[t], val[j]);
                if (!st[j])
                {
                    q[tt ++ ] = j;
                    if (tt == N) tt = 0;
                    st[j] = 1;
                }
            }
        }
    }
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
        cin >> val[i];
    memset(h, -1, sizeof h);
    memset(rh, -1, sizeof rh);
    while (m -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add (h, a, b), add(rh, b, a);
        if (c == 2) add(h, b, a), add(rh, a, b);
    }
    spfa(dmin, 1, h, 0);
    spfa(dmax, n, rh, 1);
    int res = 0;
    for (int i = 1; i <= n; i ++ )
        res = max(res, dmax[i] - dmin[i]);
    cout << res;
    return 0;
}

http://www.kler.cn/a/523577.html

相关文章:

  • jEasyUI 转换 HTML 表格为数据网格
  • 【电工基础】2.低压带电作业定义,范围,工作要求,电工基本工具
  • android 音视频系列引导
  • Hive:Hive Shell技巧
  • JMeter插件 Arrivals Thread Group 源码解析:实现原理与性能测试中的应用
  • 【leetcode】T1599
  • C言算法面试:分类与高频题解析
  • 【算法】快速排序1
  • 探秘 TCP TLP:从背景到实现
  • Python中的asyncio:高效的异步编程模型
  • Python设计模式 - 组合模式
  • 使用 MSYS2 qemu 尝鲜Arm64架构国产Linux系统
  • 电感的Q值+如何判断变压器好坏
  • 【题解】Codeforces Round 996 C.The Trail D.Scarecrow
  • 数据结构(精讲)----树(应用篇)
  • C++/stack_queue
  • ComfyUI中基于Fluxgym训练Flux的Lora模型
  • Spring事件驱动
  • 蛇年说蛇,平添乐趣
  • 大模型不同版本的区别解析
  • 苹果AR眼镜:产品规划与战略路线深度解析
  • 2025年美赛B题-结合Logistic阻滞增长模型和SIR传染病模型研究旅游可持续性-成品论文
  • LTV预估 | 深度学习PLTV之开山鼻祖ZILN
  • LLM - 大模型 ScallingLaws 的设计 100B 预训练方案(PLM) 教程(5)
  • SpringBoot内置Tomcat启动原理
  • FLTK - FLTK1.4.1 - demo - animated - v1