算法提高-图论-单源最短路的综合应用

news2024/12/23 18:42:34

单源最短路的综合应用

  • 单源最短路的综合应用
    • AcWing 1135. 新年好
    • AcWing 340. 通信线路
    • AcWing 342. 道路与航线
    • AcWing 341. 最优贸易

单源最短路的综合应用

AcWing 1135. 新年好

多次dijkstra求每个点到其它点的最短距离, 此时相当于建好了一张图,每个点之间的最短距离都知道了,接下来dfs搜一下怎么走最短即可

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

typedef pair<int, int> PII;
#define x first
#define y second

const int N = 5 * 1e4 + 10, M = 2 * 1e5 + 10, INF = 0x3f3f3f3f;

int dist[6][N];
int e[M], ne[M], w[M], h[N], idx;
int source[6];
bool st[N];
 int n, m;

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




void dijkstra(int start, int dist[])
{
    memset(st, 0, sizeof st);//求多次dijkstra,st每次都要初始化
    memset(dist, 0x3f, N * sizeof (int));
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    
    dist[start] = 0;//这里的dist是局部的,只不过重名了罢了,所以是一维的
        
    heap.push({dist[start], start});//PII根据第一个排序,所以dist放最前面,堆优化版dijksra就是存PII<dist, index>的
    
    while (heap.size())
    {
        PII t = heap.top();
        heap.pop();
        int ver = t.y;
        
        if(st[ver]) continue;
        st[ver] = true;
        
        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});
            }
        }
    }
}


int dfs(int u, int start, int distance)
{
    if (u == 6) return distance;//最多6层(佳佳自己的家 + 5个亲戚),为什么不是7,因为到佳佳自己的家距离为0,我们不需要计算,只需要计算5层就行,不需要真正计算6层
    
    int res = INF;
    
    for (int i = 1; i <= 5; i ++ )//遍历next是哪个站,每个i对应一个source[i]是有实际意义的
    {
        if(!st[i])
        {
            int next = source[i];
            st[i] = true;
            res = min(res, dfs(u + 1, i, distance + dist[start][next]));//start传i的原因是到了下一层之后起点就是source[i]了,只不过在本层它是next而已
            st[i] = false;//要回溯
        }
    }
    
    return res;
}


int main()
{
    memset(h, -1, sizeof h);
    
   
    cin >> n >> m;
    source[0] = 1;
    for (int i = 1; i <= 5; i ++ ) cin >> source[i];

    for (int i = 0; i < m; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c);
    }
    
    for (int i = 0; i < 6; i ++ ) dijkstra(source[i], dist[i]);
    
    memset(st, 0, sizeof st);//dijkstra和dfs共用一个st数组,因此做完dij后要重置st数组给dfs遍历用
    
    cout << dfs(1, 0, 0);//1指的是第一层,0指的是start是source[0]也就是车站1(佳佳的家),0指的是当前距离为0
    return 0;
}

AcWing 340. 通信线路

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

const int N = 1e3 + 10, M = 2 * 1e4 + 10;

int n, m, k;
bool st[N];
int dist[N];
deque<int> q;
int e[M], h[N], w[M], ne[M], idx;

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

bool check(int bound)
{
    memset(st, 0, sizeof st);//因为进行多从check,所以要重置
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    q.push_back(1);
    
    while (q.size())
    {
        int t = q.front();
        q.pop_front();
        
        if (st[t]) continue;
        st[t] = true;
        
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i], v = w[i] > bound;//如果大于,改边的边权就是1,代表当前大于mid的边增加1
            if (dist[j] > dist[t] + v)
            {
                dist[j] = dist[t] + v;
                if (v == 0) q.push_front(j);
                else q.push_back(j);
            }
        }
    }
    return dist[n] <= k;//判断到n点的路径大于mid的边是否<=k
}

int main()
{
    cin >> n >> m >> k;
    
    memset(h, -1, sizeof h);
    
    for (int i = 0; i < m; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c);
    }
    
    int l = 0, r = 1e6 + 1;//为什么不是1-1e6,l取0是因为答案可能取0,比如3条边 k =3,那么答案就不用付钱。 
                           //答案可能取1e6,假如第k+1条边正好是1e6,但是题目有不连通的情况,因此取1e6+1,判断是否联通
    
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    
    if (r == 1e6 + 1) cout << "-1";
    else cout << r;
    return 0;
}

AcWing 342. 道路与航线

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

using namespace std;
typedef pair<int, int> PII;
#define x first
#define y second

const int N = 25000 + 10, M = 3 * 5 * 1e4 + 10, INF = 0x3f3f3f3f;

int h[N], e[M], w[M], ne[M], idx;

int id[N], din[N];
vector<int> block[N];

bool st[N];
int dist[N];
queue<int> q;//存储团

int n, mR, mP, S;
int bcnt;

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

void dfs(int u, int bid)
{
    id[u] = bid, block[bid].push_back(u);//记录每个团内有哪些点,有多个团,因此vector是二维的
    
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (id[j] == 0) dfs(j, bid);
    }
}


void dijkstra(int bid)
{
    //虽然多次进行dijkstra,但是每次dij遍历的点的下标都是不同的,因此st数组不用重置
    priority_queue<PII, vector<PII>, greater<PII>> heap;//每个团都用一个堆去存这个团内的点
    
    for (auto u : block[bid])
        heap.push({dist[u], u});
    
    while (heap.size())
    {
        PII t = heap.top();
        heap.pop();
        int distance = t.x, ver = t.y; 
        if (st[ver]) continue;
        st[ver] = true;
        
        for (int i = h[ver]; ~i; i = ne[i])
        {
            int j = e[i];
            if (id[ver] != id[j] && -- din[id[j]] == 0) q.push(id[j]);//如果两点不在一个团内,将那个点的入度减1,如果为0,则加入拓扑序列中
            if (dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                if (id[ver] == id[j]) heap.push({dist[j], j});//如果这两个点在一个团内,再将j点加入当前团的堆
            }
        }
    }
}


//为什么按拓扑序会得到最少花费,因为每次入队也就是允许访问的节点的入度为0,
//这保证我们到此点的所有路径已经被走过并且在此过程中不断更新最短距离,
//当我们开始访问入度为0的点时,已经没有其他到此点的路径去更新它的最短距离

//(没太懂下面这句话啥意思)
//这道题没有存团之间的最短距离,也没什么意义,
//而是当dij遍历到有向边时更新访问到的另一个团中点的最短距离,
//实际上与上面的类似,也可以保证当访问每个入度为0的点时,
//团类的所有点没有其他没有访问过的从其他团到此点的路径
void topsort()
{
    memset(dist, 0x3f, sizeof dist);
    dist[S] = 0;
    
    for (int i = 1; i <= bcnt; i ++ )
    {
        if (din[i] == 0) q.push(i);//入度为0的团就加入拓扑序列中
    }
    
    while (q.size())//遍历每个团
    {
        int t = q.front();
        q.pop();
        
        dijkstra(t);//dij每个团的同时更新拓扑序列 if (id[ver] != id[j] && -- din[id[j]] == 0) q.push(id[j]);
    }
}

int main ()
{
    cin >> n >> mR >> mP >> S;
    memset(h, -1, sizeof h);
    
    for (int i = 0; i < mR; i ++ )
    {
        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] == 0)
        {
            id[i] = ++ bcnt;
            dfs(i, bcnt);
        }
    }
    
    for (int i = 0; i < mP; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add (a, b, c);
        din[id[b]] ++;//P是航线,连接各个团,b是单向边的目的地,因此b所在的团入度+1
    }
    
    topsort();
    
    for (int i = 1; i <= n; i ++ )
    {
        if (dist[i] > INF / 2) cout << "NO PATH" << endl;//因为有负边,所以只要判断结果大于一个比较大的数就说明不连通
        else cout << dist[i] << endl;
    }
    return 0;
}

AcWing 341. 最优贸易

一篇博客解释了为什么一个正向建图求最小值,反向建图求最大值
根本思想是保证1到n的买卖是连通的
在这里插入图片描述

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e5 + 10, M = 2 * 2 * 5 * 1e5 + 10;//题目给的有的是双向边有的是单向边,同时我们需要建两个图再乘2

int q[N], dmin[N], dmax[N];
int n, m;
int w[N];//存储的是每个城市的水晶球价格,而不是边权,所以开N的大小
int rh[N], h[N], e[M], ne[M], idx;
bool st[N];


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

void spfa(int h[], int dist[], int type)
{
    int hh = 0, tt = 1;
    
    if (type == 0)
    {
        memset(dist, 0x3f, sizeof dmin);//或者sizeof int
        dist[1] = w[1];
        q[0] = 1;
    }
    else
    {
        memset(dist, -0x3f, sizeof dmax);
        dist[n] = w[n];
        q[0] = n;
    }
    
    while (hh != tt)
    {
        int t = q[hh ++ ];
        if (hh == N) hh = 0;
        st[t] = false;
        
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (type == 0 && dist[j] > min(dist[t], w[j]) || type == 1 && dist[j] < max(dist[t], w[j]))
            {
                if (type == 0) dist[j] = min(dist[t], w[j]);
                else dist[j] = max(dist[t], w[j]);
                
                if (st[j] == 0)
                {
                    q[tt ++] = j;
                    if (tt == N) tt = 0;
                    st[j] = true;
                }
            }
        }
    }
    
}


int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) cin >> w[i];
    
    memset(h, -1, sizeof h), memset(rh, -1, sizeof rh);
    for (int i = 0; i < m; i ++ )
    {
        int a, b, type;
        cin >> a >> b >> type;
        add(h, a, b), add(rh, b, a);
        if (type == 2)
            add(h, b, a), add(rh, a, b);
    }
    
    spfa(h, dmin, 0);
    spfa(rh, dmax, 1);
    
    int res = 0;
    for (int i = 1; i <= n; i ++ )
        res = max(res, (dmax[i] - dmin[i]));//正向图遍历从1到i可以买入水晶球的最小值,反向图遍历从n到i可以卖出水晶求的最大值,两者求差
    cout << res;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/630949.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

http客户端Fegin

1.RestTemplate方式调用存在的问题 代码可读性差&#xff0c;编程体验不统一 参数复杂URL难以维护 2.Feign的介绍 Feign是声明式的http客户端&#xff08;只需要将发http请求的信息写出即可&#xff09; 主要基于SpringMvc的注解来声明远程调用的信息&#xff1a; 步骤&…

(十二)K8S可视化工具Rancher部署项目应用实战

1.Rancher部署springboot私有镜像 连接私有镜像操作步骤 1.进入资源>>密文 2.进入镜像库凭证列表&#xff0c;点击添加凭证 3.输入凭证名称&#xff0c;选择自定义&#xff0c;填入自己的私有镜像仓库地址&#xff0c;这里使用的是阿里云&#xff0c;输入用户名和密码…

Zoho CRM SDK

前言 因早期公司的业务在Zoho CRM&#xff0c;现在孵化出自己的想法&#xff0c;想着能把一部分CRM的数据同步导出来&#xff0c;故研究Zoho CRM SDK的接入方法。虽说在文档上都有提及&#xff0c;但有部分细节不甚明了&#xff0c;也是一遍遍尝试出来的&#xff0c;所以分享出…

【Vue】六:路由(上)使用路由 多级路由

文章目录 1.多页面应用2. 单页面应用&#xff08;使用路由&#xff09;3. 多级路由 1.多页面应用 2. 单页面应用&#xff08;使用路由&#xff09; &#xff08;1&#xff09;安装vue-router插件 vue2 要安装 vue-router3 npm i vue-router3vu3 要安装 vue-router4 npm i vue…

字节薪酬体系被曝光,我真的酸了....

曾经的互联网是PC的时代&#xff0c;随着智能手机的普及&#xff0c;移动互联网开始飞速崛起。而字节跳动抓住了这波机遇&#xff0c;2015年&#xff0c;字节跳动全面加码短视频&#xff0c;从那以后&#xff0c;抖音成为了字节跳动用户、收入和估值的最大增长引擎。 自从字节…

【Java】数组详解

文章目录 一、数组的基本认识1.1 数组的概念1.2数组的创建与初始化1.3 数组的使用 二、数组的类型 — 引用类型2.1 JVM 内存分布2.2 什么是引用类型2.3 基本类型变量与引用类型变量的区别2.4 Java 中的 null 三、数组的应用3.1 保存数据3.2 函数参数3.3 函数返回值 一、数组的基…

STL——string和vector容器

初识STL **STL的基本概念****vector容器存放内置数据类型****在vector容器中存放自定义数据类型****vector容器嵌套vector容器****string容器——构造函数****string容器——赋值操作****string容器——字符串拼接****string容器——字符串的查找和替换****string容器——字符串…

Java阶段四Day01

Java阶段四Day01 文章目录 Java阶段四Day01Security框架通配符Vue脚手架 Vue-cli关于VUE关于VUE Cli创建Vue Cli工程解决端口被占用 Vue工程的工程结构[.idea]【重要】[node_modules]【重要】[public]favicon.icoindex.html [src][assets][compnents]【重要】[router][store]【…

Kafka详解(一)

第1章 Kafka概述 1.1 定义 1.2 消息队列 目前企业中比较常见的消息队列产品主要有Kafka、ActiveMQ、RabbitMQ、RocketMQ等。Message Queue ② 在大数据场景主要采用Kafka作为消息队列 ② 在JavaEE开发中主要采用ActiveMQ、RabbitMQ、RocketMQ Kafka存储数据&#xff0c;且保证…

虚函数+多态实现原理(一个冷门知识)

目录 多态实现 虚函数定义 先说原理 抛出问题 探究多态底层 冷门知识 多形态的大海 多态实现 完成类多态体现&#xff0c;多态两个条件: 虚函数重写 父类指针或者引用去调用虚函数。 虚函数定义 虚函数重写/覆盖条件 : 函数 三同 (函数名、参数、返回值) 不符合重写&…

【板栗糖GIS】——如何使用插件将微信读书笔记同步到notion

【板栗糖GIS】——如何使用插件将微信读书笔记同步到notion 注&#xff1a;本文创意以及插件开发皆是B站的【三此君】 视频链接如下&#xff1a; 全网唯一支持图文导出的微信读书插件&#xff0c;升级啦&#xff01;主打一个惊艳。_哔哩哔哩_bilibili &#xff0c;我只是记录…

阿里内部人手一份的Spring Cloud Alibaba手册

“微服务架构经验你有吗&#xff1f;” 前段时间一个朋友去面试&#xff0c;阿里面试官一句话问倒了他。实际上&#xff0c;不在 BAT 这样的大厂工作&#xff0c;是很难接触到支撑千亿级流量微服务架构项目的。但也正是这种难得&#xff0c;让各个大厂都抢着要这样的人才&…

linux(信号结尾)

目录&#xff1a; 1.可重入函数 2.volatile关键字 3.SIGCHLD信号 -------------------------------------------------------------------------------------------------------------------------------- 1.可重入函数----------用来描述一个函数的特点的 1.在单进程当中也存…

【数据结构】图的遍历、图的应用

以下是对王道数据结构图的部分选择题的纠错 图的遍历 对于一个非连通无向图G&#xff0c;采用DFS访问所有顶点&#xff0c;在DFSTraverse函数中调用DFS的次数正好等于连通分量个数 一次遍历必然会将一个连通图中的所有顶点都访问到&#xff0c;对于已被访问的顶点不在调用DFS&…

APP测试中ios和Android的区别是什么~

01、常识性区别 02、导航方式 iOS&#xff1a;Tab放在页面底部&#xff0c;不能通过滑动来切换&#xff0c;只能点击。也有放在上面的&#xff0c;也不能滑动&#xff0c;但有些Tab本身可以滑动&#xff0c;比如天猫的。还有新闻类的应用。 Android&#xff1a;一般放在页面…

数据库实验五 函数

任务描述 本关任务&#xff1a;对表达式取整 相关知识 四舍五入的函数 ROUND(X,D) 返回X&#xff0c;其值保留到小数点后D位&#xff0c;而第D位的保留方式为四舍五入。 若D的值为0,则对小数部分四舍五入。 若将D设为负值&#xff0c;保留X值小数点左边的D位 TRUNCATE(X,D)…

《剑指 Offer--LeetCode 学习计划》-- 字符串

剑指 Offer 05. 替换空格&#xff08;Easy&#xff09; 题目描述 请实现一个函数&#xff0c;把字符串 s 中的每个空格替换成"%20"。限制&#xff1a;0 < s 的长度 < 10000。 举例说明 示例 1&#xff1a; 输入&#xff1a;s “We are happy.”。输出&…

K210图像检测(1~8)数字卡片识别

前言 第一次使用该平台。想先找一个简单的识别&#xff0c;来走走流程。就想到了&#xff0c;前几年的送药小车的数字卡片识别。花了半天收集标记图片。在运行时要注意摄像头与数字卡片的高度。不过也有些不足&#xff0c;可能是收集某个数字的训练集的时候&#xff0c;拍摄高度…

文件智能归类,让文件分类变得简单易行

在数字化信息时代&#xff0c;我们经常需要处理各种类型的文件&#xff0c;如文档、图片、视频等&#xff0c;而这些文件可能存在于不同的文件夹、不同的磁盘之间&#xff0c;管理起来十分繁琐。为了解决这个问题&#xff0c;文件智能归类管理应运而生。这种文件管理方式采用智…

公司新招了几个00后,我愿称之为卷王之王

前几天我们公司一下子也来了几个新人&#xff0c;这些年轻人是真能熬啊&#xff0c;本来我们几个老油子都是每天稍微加会班就打算走了&#xff0c;这几个新人一直不走&#xff0c;搞得我们也不好走。 2023年秋招就要开始了&#xff0c;最近内卷严重&#xff0c;各种跳槽裁员&a…