图论(3)单源最短路拓展

news2024/11/16 11:55:16

活动 - AcWing

acw1137

如果我们确定了起点,那么就是一个模板题

选起点有两个办法:1.选取虚拟原点,连接家附近的所有车站。直接以虚拟原点作为我起点跑最短路即可。2.反向建图,取终点到家附近所有车站的dist,取min即可。 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N =1010,M=20010,INF=0x3f3f3f3f;

int n,m,T;
int h[N],e[M],ne[M],w[M],idx;
int dist[N],q[N];
bool st[N];
//虚拟原点连家附近的车站  或者反向建图
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

void spfa()
{
    int scnt;
    cin>>scnt;
    memset(dist,0x3f,sizeof dist);
    int hh=0,tt=0;
    while(scnt--)
    {
        int u;
        cin>>u;
        dist[u]=0;
        q[tt++]=u;
        st[u]=true;
    }

    while(hh!=tt)
    {
        int t=q[hh++];
        if(hh==N) hh=0;

        st[t]=false;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                if(!st[j])
                {
                    q[tt++]=j;
                    if(tt==N) tt=0;
                    st[j]=true; 
                }

            }
        }
    }
}


int main()
{
    while(scanf("%d%d%d",&n,&m,&T)!=-1)
    {
        memset(h,-1,sizeof h);
        idx=0;
        while(m--)
        {
            int a,b,c;
            cin>>a>>b>>c;
            add(a,b,c);
        }
        spfa();
        if(dist[T]==INF) dist[T]=-1;
        cout<<dist[T]<<endl;
    }
}

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4338580/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

acw1131

思路1:

来自:AcWing 1131. 拯救大兵瑞恩(普通BFS解决) - AcWing 

在直接bfs可做的基础上,引入了“钥匙”和门这个因素。可以想到暴力做法,直接在状态中加入“持有钥匙”这一维度,现在题目就转化为了从状态f[1][0]转化为f[n*m][…]状态的最小步数了。

我们走到有钥匙的格子上,并不用考虑要不要拿钥匙,拿钥匙又不会增加成本,只管拿就行。因此,转移到某个格子时,直接计算下这个格子的状态,格子上有钥匙就在之前状态基础上加上这个钥匙,没有钥匙就继承之前的钥匙状态。这样一来,问题中就不存在边权为0的边了,只要状态转移了,步长都是加一,普通的BFS就可以解决了。

细节:二维坐标压缩成一个数,这样可以g[a][b]表示a到b之间的边了。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N =105,M=12;
typedef pair<int,int> PII;

int g[N][N],key[N],dist[N][1<<M];
bool st[N][1<<M];
int n,m,p,k,s;

int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};


int get(int x,int y)//坐标的状态压缩
{
    return (x-1)*m+y;
}


int bfs()
{
    queue<PII> q;
    q.push({1,key[1]});
    st[1][key[1]]=true;
    memset(dist,0x3f,sizeof dist);
    dist[1][key[1]]=0;
    
    while(q.size())
    {
        auto t=q.front();
        q.pop();
        int ver=t.first,state=t.second;
        int x=(ver-1)/m+1,y=(ver-1)%m+1;
        
        
        for(int i=0;i<4;i++)
        {
            int a=x+dx[i],b=y+dy[i];
            int s=state,p=get(a,b);
            if(!a||!b||a>n||b>m||!g[ver][p]) continue;
            if(g[ver][p]!=-1)
                if(!(s>>g[ver][p]&1)) continue;
            
            s|=key[p];
            if(!st[p][s])
            {
                q.push({p,s});
                st[p][s]=true;
                dist[p][s]=dist[ver][state]+1;
            }
            if(p==n*m) return dist[p][s];
            
        }
    }
    return -1;
}


int main()
{
    cin>>n>>m>>p;
    cin>>k;
    memset(g,-1,sizeof g);
    for(int i=0;i<k;i++)
    {
        int x1,y1,x2,y2,z;
        cin>>x1>>y1>>x2>>y2>>z;
        int p1=get(x1,y1),p2=get(x2,y2);
        g[p1][p2]=g[p2][p1]=z;
    }
    
    cin>>s;
    for(int i=0;i<s;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        key[get(x,y)] |= 1<<z;
    }
    cout<<bfs()<<endl;
}

思路2:

 

 

把捡钥匙看做一次移动,边权为0。格子间的移动视为一次移动,边权是1,然后使用双端队列bfs。线性复杂度。

小结:

思考图论问题有时候和dp十分相似。思考问题前,我们可以先把图假设为拓扑图,然后思考可行的dp方式,如状态表示和状态转移。

但是实际上图是可能存在环的,因此直接dp这种线性的方式不可行,但是可以借助图论算法框架来处理。某些图论算法还具有拓扑图性质。后续还会数次讲到类似思想。 

acwing1134最短路计数

首先用dp的方式思思考一下:

如果我们要计算i点的最短路方案,集合可以按照从哪个点转移到i点来划分。状态计算也非常简单,,,设i点可以由k1,k2,,kn点转移过去。则状态转移方程为:if(dist(ki)+w(i)==dist(i) cnt(i)+=cnt(ki)。  

为此,我们要知道所有ki点的最短路dist。我们发现一个性质,当我们要求状态state的时,状态集合里的所有值我们需要事先知道。这就是一个拓扑性质。要在计算最短路时统计方案数,则要求具备拓扑性质(最短路树),那么跑哪个算法满足我们的要求呢?

 抽象成拓扑图:跑一遍可以跑出最短路树,无环。判断方式:求解每个点的dist不互相依赖。

BFS 只入队一次,出队一次。可以抽象成拓扑图, 因为它可以保证被更新的点的父节点一定已经是最短距离了,并且这个点的条数已经被完全更新过了。这个性质是核心性质。
dijkstra 每个点只出队一次。也可以抽象成拓扑图, 同理由于每一个出队的点一定已经是最短距离,并且它出队的时候是队列中距离最小的点,这就代表他的最短距离条数已经被完全更新了,所以构成拓扑性质。
bellman_ford算法 spfa 本身不具备拓扑序,因为更新它的点不一定是最短距离,所以会出错。

当然spfa是可以做的,不过需要先把最短路,再统计方案。 

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;

const int N =100010,M=400010,mod=100003;

int h[N],e[M],ne[M],idx;
int n,m;
int dist[N],cnt[N];

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

void bfs()
{
    memset(dist,0x3f,sizeof dist);
    queue<int> q;
    q.push(1);

    cnt[1]=1;
    dist[1]=0;

    while(q.size())
    {
        int t=q.front();
        q.pop();

        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+1)
            {
                dist[j]=dist[t]+1;
                q.push(j);
                cnt[j]=cnt[t];
            }
            else if(dist[j]==dist[t]+1)
            {
                cnt[j]=(cnt[j]+cnt[t])%mod;
            }

        }
    }

}


int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=0;i<m;i++)
    {
        int a,b;
        cin>>a>>b;
        add(a,b),add(b,a);
    }

    bfs();
    for(int i=1;i<=n;i++) cout<<cnt[i]<<endl;
    return 0;


}

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4340742/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

acwing383 单源次最短路

 本题就是上一题的拓展,即统计次短路条数。如果次短路比最短路多1,则答案加上次短路条数。

同理可知,求次短路,bfs和dijkstra仍然具有拓扑性质。因为不可能存在环形依赖,一个点不可能反回去更新前一个点的次短路(因为这样次短路就会变长,显然是不可能的)。因此可以边算最短路、次短路,边统计最短路和次短路的方案。

结合第2道题,我们需要加入一维状态表示最短,次短。

状态转移:初始状态:最短路为0,没有次短路。最短路方案为1,没有次短路方案。

计算:如果新路dist比最短路小,最短路更新放入堆中继续搜,原最短路沦为次短路,次短路被更新,放入堆中继续搜。

如果与最短一样大,统计方案

如果比次短小,比最短大,更新次短

如果和次短一样大,收钱。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

const int N =1010,M=20010;

struct Ver{
    int id,type,dist;
    bool operator> (const Ver &W) const
    {
        return dist > W.dist;
    }
};
int h[N],e[M],ne[M],w[M],idx;
int n,m,S,T;
int cnt[N][2],dist[N][2];
bool st[N][2];

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

int dijkstra()
{
    memset(cnt,0,sizeof cnt);
    memset(st,false,sizeof st);
    memset(dist,0x3f,sizeof dist);

    dist[S][0]=0,cnt[S][0]=1;
    priority_queue<Ver, vector<Ver>, greater<Ver> > heap;
    heap.push({S,0,0});

    while(heap.size())
    {
        auto t=heap.top();
        heap.pop();
        int ver=t.id,type=t.type,distance=t.dist,count=cnt[ver][type];

        if(st[ver][type]) continue;
        st[ver][type]=true;
        for(int i=h[ver];~i;i=ne[i])
        {
            int j=e[i];
            if(dist[j][0]>distance+w[i])
            {
                dist[j][1]=dist[j][0];
                cnt[j][1]=cnt[j][0];
                heap.push({j,1,dist[j][1]});
                dist[j][0]=distance+w[i];
                cnt[j][0]=count;
                heap.push({j,0,dist[j][0]});
            }
            else if(dist[j][0]==distance+w[i]) cnt[j][0]+=count;
            else if(dist[j][1]>distance+w[i]) 
            {
                dist[j][1]=distance+w[i];
                cnt[j][1]=count;
                heap.push({j,1,dist[j][1]});
            }
            else if(dist[j][1]==distance+w[i]) cnt[j][1]+=count;
        }
    }

    int res=cnt[T][0];
    if(dist[T][1]==dist[T][0]+1) res+=cnt[T][1];

    return res;
}


int main()
{
    int cases;
    cin>>cases;
    while(cases--)
    {
        scanf("%d%d",&n,&m);
        memset(h,-1,sizeof h);
        idx=0;

        while(m--)
        {
            int a,b,c;
            cin>>a>>b>>c;
            add(a,b,c);
        }
        scanf("%d%d",&S,&T);
        cout<<dijkstra()<<endl;
    }
    return 0;
}


作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4372015/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 

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

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

相关文章

JVM垃圾回收器-Serial回收器:串行回收

文章目录学习资料垃圾回收器Serial回收器&#xff1a;串行回收学习资料 【尚硅谷宋红康JVM全套教程&#xff08;详解java虚拟机&#xff09;】 【阿里巴巴Java开发手册】https://www.w3cschool.cn/alibaba_java JDK 8 JVM官方手册 垃圾回收器 Serial回收器&#xff1a;串行回…

JVM垃圾回收相关算法-垃圾清除阶段

文章目录学习资料垃圾回收相关算法垃圾清除阶段标记-清除&#xff08;Mark - Sweep&#xff09;算法复制算法标记-压缩&#xff08;或标记-整理、Mark - Compact&#xff09;算法分代收集算法增量收集算法分区算法学习资料 【尚硅谷宋红康JVM全套教程&#xff08;详解java虚拟…

助力工业物联网,工业大数据项目介绍及环境构建【三】

01&#xff1a;业务系统结构 目标&#xff1a;了解一站制造中的业务系统结构 实施 数据来源 业务流程 油站站点联系呼叫中心&#xff0c;申请工单呼叫中心分派工单给工程师工程师完成工单工程师费用报销呼叫中心回访工单 ERP系统&#xff1a;企业资源管理系统&#xff0c;存…

RHCSE第一天(Linux的例行性工作)

文章目录Linux搭建服务器的准备工作第一章 Linux的例行性工作1.1 单一执行的例行性工作at1.1.1 at命令的实际工作过程1.1.2 at命令详解1.2 循环执行的例行性工作1.2.1 crontab命令的实际工作过程1.2.2 crontab命令详解1.3 实验实验一&#xff1a;定义三分钟之后显示hello实验二…

【前端】Vue项目:旅游App-(4)TabBar:Vant库实现功能与样式

文章目录目标代码和过程Vant库引入自定义样式初步修改样式修改修改active颜色icon调大实现路由跳转效果总代码修改的文件tab-bar.vuemain.js目标 前文手写了TabBar的样式和功能&#xff0c;本篇我们用vant库重新实现这些功能。 代码和过程 Vant库引入 Vant4 官方文档 安装&…

Servlet运行原理及生命周期

Servlet运行原理及生命周期一、Servlet 运行原理1.1 Tomcat 的定位1.2 Tomcat 的伪代码1.2.1 Tomcat 初始化流程1.2.2 Tomcat 处理请求流程1.2.3 Servlet 的 service 方法的实现二、Servlet生命周期一、Servlet 运行原理 承接 Servlet基础教程&#xff1a;https://blog.csdn.n…

spring oAuth2.0

会话 用户认证通过后&#xff0c;为了避免用户的每次操作都进行认证可将用户的信息保证在会话中。会话就是系统为了保持当前用户的登录状态所提供的机制&#xff0c;常见的有基于session方式&#xff0c;基于token方式等。 基于session的认证方式&#xff1a; 用户认证成功后&a…

Qt扫盲-QSS帮助手册使用

QSS帮助手册使用一、概述1. 查找 Qt Style Sheets Reference2. 收藏一下二、Qt Assistant 查阅帮助1. List of Stylable Widgets2. List of Properties3. List of Icons4. List of Property Types5. List of Pseudo-States6. List of Sub-Controls一、概述 导言&#xff1a;这…

基于信创运维平台,实现国产化网络自动巡检

近年来&#xff0c;以工业互联网、大数据、人工智能、5G技术等为代表的新一代信息技术飞速发展&#xff0c;推动国内企业向数字化经济的变革&#xff0c;数字化变成一股不可逆转的潮流&#xff0c;也是增强企业竞争力的关键所在。北京智和信通积极探索&#xff0c;把握新一轮数…

Pytorch模型转成onnx并可视化

文章目录转换模型前提转换方法模型可视化可能出现的报错信息ValueError: torch.nn.DataParallel is not supported by ONNX exporter, please use attribute module to unwrap model from torch.nn.DataParallel. Try torch.onnx.export(model.module, ...)RuntimeError: ONNX …

流媒体基础-RTP封装PS流

PS流格式 首条数据结构: RTP Header + PS Header +(System Header + PSM)+ PES(Header + Payload) 非首条数据结构: RTP Header + PS Header + PES(Header + Payload) 接下来记录侧重于找到 H.264 数据,所以会跳过一些暂时不关心的内容。 PS流有一个结束码 MPEG_progr…

信息学奥赛一本通 1916:【01NOIP普及组】求先序排列 | 洛谷 P1030 [NOIP2001 普及组] 求先序排列

【题目链接】 ybt 1916&#xff1a;【01NOIP普及组】求先序排列 洛谷 P1030 [NOIP2001 普及组] 求先序排列 【题目考点】 1. 二叉树 【解题思路】 已知中序、后序遍历序列&#xff0c;构建二叉树&#xff0c;而后对该二叉树做先序遍历&#xff0c;得到先序遍历序列。 该题…

质性分析软件nvivo的学习(二)

0、前言&#xff1a; 这部分内容是&#xff0c;质性分析软件nvivo的学习&#xff08;一&#xff09;的衔接内容&#xff0c;建议看完&#xff1a;质性分析软件nvivo的学习&#xff08;一&#xff09;再看这部分内容。这里的笔记都是以nvivo12作为学习案例的&#xff0c;其实不…

JVM垃圾回收的并行与并发

文章目录学习资料垃圾回收的并行与并发并发&#xff08;Concurrent&#xff09;并行&#xff08;Parallel&#xff09;并发 vs 并行总结学习资料 【尚硅谷宋红康JVM全套教程&#xff08;详解java虚拟机&#xff09;】 【阿里巴巴Java开发手册】https://www.w3cschool.cn/aliba…

RHCEansible 任务模块

文件操作模块 file模块 ansible-doc file -s path参数 &#xff1a;必须参数&#xff0c;用于指定要操作的文件或目录&#xff0c;在之前版本的ansible中&#xff0c;使用dest参数或者name参数指定要操作的文件或目录&#xff0c;为了兼容之前的版本&#xff0c;使用dest或n…

STM32的升级--ICP/ISP/IAP以及Ymodem协议分析

ICP/ISP/IAP 区别 ICP(In-Circuit Programing): 通过J-Link/SWD等下载器烧写程序&#xff0c;上位机需要借助其他硬件的参与才能更新固件&#xff0c;可以更新MCU的所有存储区域&#xff1b; ISP(In-System Programing): 通过MCU出厂时固化的一个bootloader升级程序&#xff0…

重点算法排序之快速排序、归并排序(上篇)

文章目录 一、排序的概念及常见的排序算法 二、快速排序的思想及代码详解 2、1 快速排序的思想 2、2 挖坑法 2、2、1 挖坑法实现思想 2、2、2 挖坑法举例 2、2、3 挖坑法代码实现 2、3 左右指针法 2、3、1 左右指针法实现思想 2、3、2 左右指针法举例 2、3、3 左右指针法代码…

类的成员之四:代码块

文章目录一、代码块静态代码块非静态代码块注意总结&#xff1a;由父及子&#xff0c;静态先行属性赋值总结一、代码块 1、代码块的作用&#xff1a;用来初始化类、对象 2、代码块如果有修饰的话&#xff0c;只能使用static 3、分类&#xff1a;静态代码块 vs 非静态代码块 静…

Ubuntu自动登录脚本

Ubuntu自动登录脚本一、!/usr/bin/expect -f的意义二、spawn命令行&#xff1a;三、send命令&#xff1a;四、expect五、interact命令&#xff1a;本人用xshell的SSH登录云服务器&#xff0c;需要从用户Ubuntu切换到root&#xff0c;再切换到lighthouse。编辑了一个自动切换用户…

2-1进程管理-进程与线程

文章目录1.进程2.进程控制块&#xff08;PCB&#xff09;3.进程的状态与转换4.进程通信8.线程9.线程和进程的比较10.线程的实现方式11.多线程模型20.线程的状态与转换1.进程 &#xff08;1&#xff09;进程是程序的一次执行过程 &#xff08;2&#xff09;进程是进程实体的运行…