P1629 邮递员送信

题目描述

有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间。这个邮递员每次只能带一样东西。求送完这N-1样东西并且最终回到邮局最少需要多少时间。

输入格式

第一行包括两个整数N和M。

第2到第M+1行,每行三个数字U、V、W,表示从A到B有一条需要W时间的道路。 满足1<=U,V<=N,1<=W<=10000,输入保证任意两点都能互相到达。

【数据规模】

对于30%的数据,有1≤N≤200;

对于100%的数据,有1≤N≤1000,1≤M≤100000。

输出格式

输出仅一行,包含一个整数,为最少需要的时间。

输入输出样例

输入 #1复制

5 10

2 3 5

1 5 5

3 5 6

1 2 8

1 3 8

5 3 4

4 1 8

4 5 3

3 5 6

5 4 2

输出 #1复制

83

【两种方法】

【SPFA】

SPFA

【注意注意】

这是单向路单向路单向路!!!

重要的事情说三遍!!!

(题目中没点出这点但是我的全WA经历让我深刻的认识到了这一点)

【思路】

去和来这是两个完全相反的东西

SPFA跑一个方向是很轻松的

然后另一个方向就很难办了

该怎么办呢?

n遍SPFA?

不太可能这就是一道黄题

对了!可以反向建图!

将方向反过来建出来的图就是完全相反的

某个点到x的距离恰好就是回家的最短距离

这样和正向建图一结合

就能求出去和回的最短路径了

然后求和就可以了

【完整代码】

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int Max = 100005;
struct node
{
int y;
int ne;
int z;
}a1[Max << 1],a2[Max << 1];
int n,m,x;
const int M = 1002;
int head1[M],head2[M];
int sum = 0;
void add1(int x,int y,int z)
{
a1[++ sum].y = y;
a1[sum].z = z;
a1[sum].ne = head1[x];
head1[x] = sum;
}
void add2(int x,int y,int z)
{
a2[++ sum].y = y;
a2[sum].z = z;
a2[sum].ne = head2[x];
head2[x] = sum;
} int d1[M],d2[M];
bool use[M];
void SPFA1()
{
memset(use,false,sizeof(use));
queue<int>q;
for(register int i = 1;i <= n;++ i)
d1[i] = 999999;
d1[x] = 0;
q.push(x);
while(!q.empty())
{
int qwq = q.front();
q.pop();use[qwq] = false;
for(register int i = head1[qwq];i != 0;i = a1[i].ne)
{
int awa = a1[i].y;
if(d1[awa] > d1[qwq] + a1[i].z)
{
d1[awa] = d1[qwq] + a1[i].z;
if(use[awa] == false)
{
use[awa] = true;
q.push(awa);
}
}
}
}
}
void SPFA2()
{
memset(use,false,sizeof(use));
queue<int>q;
for(register int i = 1;i <= n;++ i)
d2[i] = 999999;
d2[x] = 0;
q.push(x);
while(!q.empty())
{
int qwq = q.front();
q.pop();use[qwq] = false;
for(register int i = head2[qwq];i != 0;i = a2[i].ne)
{
int awa = a2[i].y;
if(d2[awa] > d2[qwq] + a2[i].z)
{
d2[awa] = d2[qwq] + a2[i].z;
if(use[awa] == false)
{
use[awa] = true;
q.push(awa);
}
}
}
}
} int main()
{
scanf("%d%d",&n,&m);
x = 1;
int xx,yy,zz;
for(register int i = 1;i <= m;++ i)
{
scanf("%d%d%d",&xx,&yy,&zz);
add1(xx,yy,zz);
add2(yy,xx,zz);
}
SPFA1();
SPFA2();
int MM = 0;
for(register int i = 1;i <= n;++ i)
MM = MM + d1[i] + d2[i];
cout << MM << endl;
return 0;
}

【dijkstra】

dijkstra + 堆优化

【说在前面的话】

通过这道题发现我真是个可爱的孩子

用dijkstra做的时候

开开心心打一个反向建图

轻轻松松提交上去

然后拿了70分……

堆优化之后的复杂度是O((N+M)logN)完全不会超时

然后我就开始思考各种可能出现的玄学问题

最后焦灼了一个小时

终于发现是数组开小了……

鬼知道为什么数组开小会超时……

【核心思路】

有去有回

如果只是去的话单源最短路径就可以轻轻松松解决掉

但是来回的话

首先想到的是多源最短路径

但是好像跑不过去

这就用到了和上面SPFA一样的思路了

就是反向建图

这样只需要两遍dijkstra就可以解决掉

最后再求个和就没有问题了

【完整代码】

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int Max = 100004;
const int M = 1003;
struct point
{
int w;
int x;
bool operator < (const point & xx)const
{
return xx.w < w;
}
};
int n,m;
struct node
{
int y,ne;
int z;
}a1[Max << 1],a2[Max << 1];
int sum1 = 0,sum2 = 0;
int head1[M],head2[M]; inline int read()
{
int x=0; char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
return x;
} void add1(int x,int y,int z)
{
a1[++ sum1].y = y;
a1[sum1].z = z;
a1[sum1].ne = head1[x];
head1[x] = sum1;
}
void add2(int x,int y,int z)
{
a2[++ sum2].y = y;
a2[sum2].z = z;
a2[sum2].ne = head2[x];
head2[x] = sum2;
}
int dis1[M],dis2[M];
bool use1[M],use2[M];
priority_queue<point>q;
void dj1()
{
for(register int i = 2;i <= n;++ i)
dis1[i] = 999999;
dis1[1] = 0;
q.push((point){0,1});
while(!q.empty())
{
point qwq = q.top();
q.pop();
int x = qwq.x;
if(use1[x] == true)
continue;
use1[x] = true;
for(register int i = head1[x];i != 0;i = a1[i].ne)
{
int awa = a1[i].y;
if(dis1[awa] > dis1[x] + a1[i].z)
{
dis1[awa] = dis1[x] + a1[i].z;
if(use1[awa] == false)
q.push((point){dis1[awa],awa});
}
}
}
} void dj2()
{
for(register int i = 2;i <= n;++ i)
dis2[i] = 999999;
dis2[1] = 0;
q.push((point){0,1});
while(!q.empty())
{
point qwq = q.top();
q.pop();
int x = qwq.x;
if(use2[x] == true)
continue;
use2[x] = true;
for(register int i = head2[x];i != 0;i = a2[i].ne)
{
int awa = a2[i].y;
if(dis2[awa] > dis2[x] + a2[i].z)
{
dis2[awa] = dis2[x] + a2[i].z;
if(use2[awa] == false)
q.push((point){dis2[awa],awa});
}
}
}
} int main()
{
n = read();m = read();
int x,y,z;
for(register int i = 1;i <= m;++ i)
{
x = read();y = read();z = read();
add1(x,y,z);add2(y,x,z);
}
dj1();
dj2();
int ans = 0;
for(register int i = 2;i <= n;++ i)
ans += dis1[i] + dis2[i];
cout << ans << endl;
return 0;
}

洛谷 P1629 邮递员送信 题解的更多相关文章

  1. yzoj P1412 & 洛谷P1629 邮递员送信 题解

    有一个邮递员要送东西,邮局在结点1.他总共要送N-1样东西,其目的地分别是2~N.由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间.这个邮递员每次只能带一 ...

  2. 洛谷 P1629 邮递员送信-反向建边

    洛谷 P1629 邮递员送信 题目描述: 有一个邮递员要送东西,邮局在节点 11.他总共要送 n-1n−1 样东西,其目的地分别是节点 22 到节点 nn.由于这个城市的交通比较繁忙,因此所有的道路都 ...

  3. 洛谷——P1629 邮递员送信

    P1629 邮递员送信 题目描述 有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N.由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要 ...

  4. 洛谷P1629 邮递员送信

    题目描述 有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N.由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间.这个邮递员每 ...

  5. 洛谷 P1629 邮递员送信

    题目描述 有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N.由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间.这个邮递员每 ...

  6. 洛谷—— P1629 邮递员送信

    https://www.luogu.org/problem/show?pid=1629 题目描述 有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N.由于这个城市的交通比 ...

  7. 洛谷P1629 邮递员送信 最短路-Djistra

    先上一波题目qwq https://www.luogu.org/problem/P1629· 复习了一波 dijstra 的 priority_queue(优先队列)优化的写法 tips: 求单项路中 ...

  8. 洛谷P2832 行路难 分析+题解代码【玄学最短路】

    洛谷P2832 行路难 分析+题解代码[玄学最短路] 题目背景: 小X来到了山区,领略山林之乐.在他乐以忘忧之时,他突然发现,开学迫在眉睫 题目描述: 山区有n座山.山之间有m条羊肠小道,每条连接两座 ...

  9. 【洛谷P3960】列队题解

    [洛谷P3960]列队题解 题目链接 题意: Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有 n×m ...

随机推荐

  1. 异或序列 [set优化DP]

    也许更好的阅读体验 \(\mathcal{Description}\) 有一个长度为 \(n\)的自然数序列 \(a\),要求将这个序列分成至少 \(m\) 个连续子段 每个子段的价值为该子段的所有数 ...

  2. ubuntu系统下防火墙简单使用

    apt-get install ufw      安装防火墙sudo ufw enable|disable|status         开启/关闭/查看防火墙状态sudo ufw allow 22/ ...

  3. 2. matplotlib绘制散点图

    与绘制直线图的唯一区别:plt.scatter # coding=utf-8 from matplotlib import pyplot as plt from matplotlib import f ...

  4. v-for循环列表,展开样式随手风琴

    <div class="product_box" v-for="(item,index) of kinds" :key="index" ...

  5. python多任务的实现:线程,进程,协程

    什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务.打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,至少同时有3个任务正在运行.还有很多任务悄悄地在后 ...

  6. Flink源码分析 - 剖析一个简单的Flink程序

    本篇文章首发于头条号Flink程序是如何执行的?通过源码来剖析一个简单的Flink程序,欢迎关注头条号和微信公众号"大数据技术和人工智能"(微信搜索bigdata_ai_tech) ...

  7. python-pyhon与模块安装

    python 安装Python,配置环境变量,路径为python安装路径,如D:\pythoncmd中输入python可以识别则安装成功 pip升级指令python -m pip install -- ...

  8. VS Code好用到飞起的配置设置

    Visual Studio Code是一个轻量级但功能强大的源代码编辑器,可在桌面上运行,适用于Windows,macOS和Linux.它内置了对JavaScript,TypeScript和Node. ...

  9. 嵌入式Linux框架的理解

    从事嵌入式linux工作也几年了,如果算上大学期间的自学,那么也算是个工程师了.期间写过底层bootloader.内核的驱动和上层应用程序.对于芯片内部的模块也在大学时候用fpga的verilog玩过 ...

  10. OpenStack核心组件-glance镜像服务

    1. glance介绍 Glance是Openstack项目中负责镜像管理的模块,其功能包括虚拟机镜像的查找.注册和检索等. Glance提供Restful API可以查询虚拟机镜像的metadata ...