HDU 2680 最短路 迪杰斯特拉算法 添加超级源点

时间:2022-05-19 14:57:02

Choose the best route

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5273    Accepted Submission(s): 1680

Problem Description
One day , Kiki wants to visit one of her friends. As she is liable to carsickness , she wants to arrive at her friend’s home as soon as possible . Now give you a map of the city’s traffic route, and the stations which are near Kiki’s home so that she can take. You may suppose Kiki can change the bus at any station. Please find out the least time Kiki needs to spend. To make it easy, if the city have n bus stations ,the stations will been expressed as an integer 1,2,3…n.
 
Input
There are several test cases. 
Each case begins with three integers n, m and s,(n<1000,m<20000,1=<s<=n) n stands for the number of bus stations in this city and m stands for the number of directed ways between bus stations .(Maybe there are several ways between two bus stations .) s stands for the bus station that near Kiki’s friend’s home.
Then follow m lines ,each line contains three integers p , q , t (0<t<=1000). means from station p to station q there is a way and it will costs t minutes .
Then a line with an integer w(0<w<n), means the number of stations Kiki can take at the beginning. Then follows w integers stands for these stations.
 
Output
The output contains one line for each data set : the least time Kiki needs to spend ,if it’s impossible to find such a route ,just output “-1”.
 
Sample Input
5 8 5
1 2 2
1 5 3
1 3 4
2 4 7
2 5 6
2 3 5
3 5 1
4 5 1
2
2 3
4 3 4
1 2 3
1 3 4
2 3 2
1
1
 
Sample Output
1
-1
 

虽然又是迪杰斯特拉算法,但是还是搬到博客上来写,是因为解题思想挺牛的。

节点为10^3,本来用迪杰斯特拉,就算没优化,也是没什么问题的,可是题目指定了终点,又给定了一系列点作为起点,我估摸着,即使代码中间优化剪枝,如果数据够强力,还是有TLE危险。(后来即使用n^2算法也跑了400+ms,所以如果按这个思路几乎是TLE的命)。

后来大神博客介绍了两种思路。。瞬间就把时间复杂度降低了。。简直碉堡了

思路1、添加超级源点。这也是我之后选择的方法

即定义一个0号点,d[0]=0,并且v[0][j]=0,j为题目指定的一系列初始点坐标,使得将那一系列起始点都统一成了一个超级源点,但是因为把v[0][j]都设置为0,所以不会对结果产生任何影响,这样,只要照着朴素的迪杰斯特拉算法敲一遍,遍历n+1个点,即可得到结果。

思路2.逆向图

因为题目不是指定了最终点只是一个点吗?何不逆向从终点出发,这样,只是把循环反转了一下,照样是朴素的迪杰斯特拉算法。。最后比较一下,出发的那一系列点的数值即可。

我用的是思路一,思路二我大概想了一下,不会太难写,除了逆向的时候注意一下细节。。对了,必须提醒一下!!!该题目的图为单向图!!!多向图就会完蛋!!!!,HDU的discuss里面说是因为英语不好,但是我仔细再读了一下,也没发现哪里说了是单向的。。而且尼玛,我严重怀疑出题者是不是自己忘了弄双向,根据生活经验,尼玛哪个城市的公交是只去不回的!!!!。。。还有必须吐槽一下今天做的其他几个最短路的题目,还都是HDU的,都尼玛是坑货题意,明明两点存在多条路径,需要在读入的时候,判断一下,。。但题目愣是一点都没说。。幸好有discuss里面早就被坑死的人提示了这里,否则我还不知道要WA多少次。。。

好了  不废话了、、、

#include <iostream>
#include <cstdio>
#include <cstring>
#define inf 100000000
using namespace std;
int d[];
int vis[];
int v[][];
int n,m,s;
int stapoint[];
int w;
int dijst()
{
int i,j,k;
memset(vis,,sizeof vis);
d[]=;//设置超级源点
for (i=; i<=w; i++)
{
v[][stapoint[i]]=; //将超级源点与已知一系列初始点的路程设置为0;
}
for (j=; j<=n; j++)
{
int mini=inf;
int loc=;
for (k=; k<=n; k++)
if (mini>d[k]&&!vis[k])
mini=d[k],loc=k;
if (mini==inf) break;
vis[loc]=;
for (k=; k<=n; k++)
{
if (d[k]>d[loc]+v[loc][k]) d[k]=d[loc]+v[loc][k];
//cout<<k<<" "<<d[k]<<endl;
}
}
if (d[s] < inf) return d[s];
else
return -;
}
int main()
{
while (scanf("%d %d %d",&n,&m,&s)!=EOF)
{
int i,j,k;
for (i=; i<=n; i++)
{
d[i]=inf;
for (j=; j<=n; j++)
v[i][j]=inf;
}
for (i=; i<=m; i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
if (v[a][b]>c)
v[a][b]=c;
//cout<<v[a][b]<<endl;
}
scanf("%d",&w);
for (i=; i<=w; i++)
scanf("%d",&stapoint[i]);
printf("%d\n",dijst());
}
return ;
}