题目描述
有一个邮递员要送东西,邮局在节点 1。他总共要送 n−1 样东西,其目的地分别是节点 2 到节点 n。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有 m 条道路。这个邮递员每次只能带一样东西,并且运送每件物品过后必须返回邮局。求送完这 n−1 样东西并且最终回到邮局最少需要的时间。
输入格式
- 第一行包括两个整数,n 和 m,表示城市的节点数量和道路数量。
- 第二行到第 (m+1) 行,每行三个整数, u, v, w,表示从 u 到 v 有一条通过时间为 w 的道路。
输出格式
- 输出仅一行,包含一个整数,为最少需要的时间。
样例
输入#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
说明
- 对于 30% 的数据, 1 ≤ n ≤ 200。
- 对于 100% 的数据,1 ≤ n ≤ 103,1 ≤ m ≤ 105,1 ≤ u,v ≤ n,1 ≤ w ≤ 104 。
- 输入保证任意两点都能互相到达。
思路
正常使用Dirskra最短路径遍历所有边更新起点到其他点的距离。但是因为本题还需要求返回起点的最短路径,并且因为边是单向的,不能原路返回,所以建议使用反向图,再求一下起点到各点的最短路径,既各点到起点的最短路径;
代码如下,详见注释:
#include <iostream>
#include <cstdio>
#define MAX 1005
using namespace std;
int dis[MAX], drs[MAX], sum=0; //两个数组一个存初始点到其他点的最短距离,一个存其他点到初始点的距离;
int u[MAX*100], v[MAX*100], w[MAX*100]; //注意数组大小,后面我才注意到是10^5,结果只有30分,卡了我贼久;
int n, m, s, check, b1=1, b2=1; //check用来检查dis[]和drs[]是否更新完;
const int inf=0x3f3f3f3f;
int main()
{
scanf("%d%d", &n, &m); //也可以用cin输入,应该没问题;
s = 1;
for(int i=1;i<=m;i++)
scanf("%d%d%d", &u[i], &v[i], &w[i]);
for(int i=1;i<=n;i++)
dis[i] = drs[i] = inf; //初始化dis[]和drs[];
dis[s] = drs[s] = 0; //设置初始点;
for(int k=1;k<n;k++) //先遍历n次;
{
check=0;
if (b1) //如果dis[]更新完了,设b1为0,就不用再遍历每条边去更新dis[]了,下面同理;
for(int i=1;i<=m;i++)
{
if(dis[v[i]]>dis[u[i]]+w[i])
{
dis[v[i]]=dis[u[i]]+w[i];
check=1;
}
}
if (check==0) b1=0;
check = 0; //记得重新设定check的值;
if (b2)
for(int i=1;i<=m;i++) //求drs[]的过程相当于反向求初始点到各点的过程;
{
if(drs[u[i]]>drs[v[i]]+w[i])
{
drs[u[i]]=drs[v[i]]+w[i];
check = 1;
}
}
if (check==0) b2=0;
}
// for(int i=1; i<=n; i++)
// {
// cout << dis[i] << " " << drs[i] << endl;
// }
for(int i=2;i<=n;i++)
sum += dis[i]+drs[i]; //dis[]和drs[]求和;
cout << sum << endl;
return 0;
}
蒟蒻一只,欢迎指正