南将军统领着N个部队,这N个部队分别驻扎在N个不同的城市。
他在用这N个部队维护着M个城市的治安,这M个城市分别编号从1到M。
现在,小工军师告诉南将军,第K号城市发生了暴乱,南将军从各个部队都派遣了一个分队沿最近路去往暴乱城市平乱。
现在已知在任意两个城市之间的路行军所需的时间,你作为南将军麾下最厉害的程序员,请你编写一个程序来告诉南将军第一个分队到达叛乱城市所需的时间。
注意,两个城市之间可能不只一条路。
1 3 8 9 8 1 2 3 1 2 1 2 3 2 1 4 2 2 5 3 3 6 2 4 7 1 5 7 3 5 8 2 6 8 2
4
这是一道非常经典的图论最短路问题!试了4种解法(Bellman-Ford,Dijkstra,Spfa,Floyd-Warshall超时)
1.Bellman-Ford
Bellman-Ford最重要的一个特点就是可以判负圈,如果图中不存在从s可达的负圈,那么最短路不会经过同一个顶点两次(也就是说最多通过M-1条边),while(true)的循环最多执行M-1次,如果存在从s可达的负圈,那么在第M次循环中也会更新d的值,因此可以检查负圈。
01.
02.
#include
03.
#include
04.
using
namespace
std;
05.
06.
const
int
INF=0x7fffffff;
07.
int
T,N,M,P,Q;
08.
int
d[1005],g[105];
09.
10.
struct
edge
11.
{
12.
int
from,to,cost;
13.
}es[200010];
14.
15.
int
bellman(
int
s)
16.
{
17.
for
(
int
i=0;i<=M;i++)
18.
d[i]=INF;
19.
d[s]=0;
20.
while
(
true
)
21.
{
22.
bool
update=
false
;
23.
for
(
int
i=0;i<2*P;i++)
24.
{
25.
edge e=es[i];
26.
if
(d[e.from]!=INF && d[e.to]>d[e.from]+e.cost)
27.
{
28.
d[e.to]=d[e.from]+e.cost;
29.
update=
true
;
30.
}
31.
}
32.
if
(!update)
33.
break
;
34.
}
35.
}
36.
37.
bool
loop()
38.
{
//判负圈
39.
for
(
int
i=0;i
40.
for
(
int
j=0;j<2*P;j++)
41.
{
42.
edge e=es[j];
43.
if
(d[e.from]!=INF && d[e.to]>d[e.from]+e.cost)
44.
{
45.
d[e.to]=d[e.from]+e.cost;
46.
if
(i==M-1)
47.
return
true
;
48.
}
49.
}
50.
return
false
;
51.
}
52.
53.
int
main()
54.
{
55.
cin>>T;
56.
while
(T--)
57.
{
58.
cin>>N>>M>>P>>Q;
59.
for
(
int
i=0;i
60.
cin>>g[i];
61.
for
(
int
i=0;i
62.
{
63.
cin>>es[i].from>>es[i].to>>es[i].cost;
64.
es[i+P].from=es[i].to;
//以Q为出发点反向建立邻接表
65.
es[i+P].to=es[i].from;
66.
es[i+P].cost=es[i].cost;
67.
}
68.
bellman(Q);
69.
int
res=INF;
70.
for
(
int
i=0;i
71.
if
(res>d[g[i]])
72.
res=d[g[i]];
73.
cout<
74.
}
75.
return
0;
76.
}
2.Dijkstra
Dijkstra算法就是先找到最短距离已经确定的顶点,从它出发更新相邻顶点的最短距离。
01.
02.
#include
03.
#include
04.
#include
05.
using
namespace
std;
06.
07.
const
int
INF=1000000;
08.
int
T,N,M,P,Q;
09.
int
d[1005],g[105];
10.
int
cost[1005][1005];
11.
bool
used[1005];
12.
13.
void
dijkstra(
int
s)
14.
{
15.
for
(
int
i=0;i<=M;i++)
16.
{
17.
d[i]=INF;
18.
used[i]=
false
;
19.
}
20.
d[s]=0;
21.
while
(
true
)
22.
{
23.
int
k=-1;
24.
for
(
int
i=1;i<=M;i++)
25.
if
(!used[i] && (k==-1 || d[i]
26.
k=i;
27.
if
(k==-1)
28.
break
;
29.
used[k]=
true
;
30.
for
(
int
i=1;i<=M;i++)
31.
d[i]=min(d[i],d[k]+cost[k][i]);
32.
}
33.
}
34.
35.
int
main()
36.
{
37.
cin>>T;
38.
while
(T--)
39.
{
40.
cin>>N>>M>>P>>Q;
41.
for
(
int
i=0;i
42.
cin>>g[i];
43.
for
(
int
i=0;i<1005;i++)
44.
for
(
int
j=0;j<1005;j++)
45.
cost[i][j]=INF;
46.
for
(
int
i=0;i
47.
{
48.
int
x,y,z;
49.
cin>>x>>y>>z;
50.
cost[x][y]=z;
//邻接矩阵
51.
cost[y][x]=z;
52.
}
53.
dijkstra(Q);
54.
int
res=INF;
55.
for
(
int
i=0;i
56.
if
(res>d[g[i]])
57.
res=d[g[i]];
58.
cout<
59.
}
60.
return
0;
61.
}
使用堆优化后,速度和内存会减少很多
01.
02.
#include
03.
#include
04.
#include
05.
#include
06.
using
namespace
std;
07.
08.
const
int
INF=1000000;
09.
int
T,N,M,P,Q;
10.
int
d[1005],g[105];
11.
typedef
pair<
int
,
int
> pa;
12.
13.
struct
edge{
14.
int
to,cost;
15.
};
16.
vector v[1005];
17.
18.
void
dijkstra(
int
s)
19.
{
20.
priority_queue,greater > que;
21.
memset
(d,INF,
sizeof
(d));
22.
d[s]=0;
23.
que.push(pa(0,s));
24.
while
(!que.empty())
25.
{
26.
pa pai=que.top();
27.
que.pop();
28.
int
k=pai.second;
29.
if
(d[k]
30.
continue
;
31.
for
(
int
i=0;i
32.
{
33.
edge e=v[k][i];
34.
if
(d[e.to]>d[k]+e.cost)
35.
{
36.
d[e.to]=d[k]+e.cost;
37.
que.push(pa(d[e.to],e.to));
38.
}
39.
}
40.
}
41.
}
42.
43.
int
main()
44.
{
45.
cin>>T;
46.
while
(T--)
47.
{
48.
cin>>N>>M>>P>>Q;
49.
memset
(v,0,
sizeof
(v));
50.
for
(
int
i=0;i
51.
cin>>g[i];
52.
for
(
int
i=0;i
53.
{
54.
edge e;
55.
int
x,y,z;
56.
cin>>x>>y>>z;
57.
e.to=y;
58.
e.cost=z;//邻接表
59.
v[x].push_back(e);
60.
e.to=x;
61.
v[y].push_back(e);
62.
}
63.
dijkstra(Q);
64.
int
res=INF;
65.
for
(
int
i=0;i
66.
if
(res>d[g[i]])
67.
res=d[g[i]];
68.
cout<
69.
}
70.
return
0;
71.
}
3.Spfa
Spfa是Bellman-Ford算法的一种队列优化实现,减少了不必要的计算
01.
02.
#include
03.
#include
04.
#include
05.
#include
06.
using
namespace
std;
07.
08.
const
int
INF=1000000;
09.
int
T,N,M,P,Q;
10.
int
d[1005],g[105],cost[1005][1005];
11.
bool
used[1005];
12.
13.
int
spfa(
int
s)
14.
{
15.
memset
(d,INF,
sizeof
(d));
16.
memset
(used,0,
sizeof
(used));
17.
queue<
int
> que;
18.
d[s]=0;
19.
used[s]=
true
;
20.
que.push(s);
21.
while
(!que.empty())
22.
{
23.
int
k=que.front();
24.
que.pop();
25.
for
(
int
i=0;i
26.
{
//存在负权时当某点的入队次数超过顶点数返回
27.
if
(d[i]>d[k]+cost[k][i])
28.
{
29.
d[i]=d[k]+cost[k][i];
30.
if
(!used[i])
31.
{
32.
que.push(i);
33.
used[i]=
true
;
34.
}
35.
}
36.
}
37.
used[k]=
false
;
38.
}
39.
}
40.
41.
int
main()
42.
{
43.
cin>>T;
44.
while
(T--)
45.
{
46.
cin>>N>>M>>P>>Q;
47.
for
(
int
i=0;i<=M;i++)
48.
for
(
int
j=0;j<=M;j++)
49.
cost[i][j]=INF;
50.
for
(
int
i=0;i
51.
cin>>g[i];
52.
for
(
int
i=0;i
53.
{
54.
int
x,y,z;
55.
cin>>x>>y>>z;
56.
cost[x][y]=z;
57.
cost[y][x]=z;
58.
}
59.
spfa(Q);
60.
int
res=INF;
61.
for
(
int
i=0;i
62.
if
(res>d[g[i]])
63.
res=d[g[i]];
64.
cout<
65.
}
66.
return
0;
67.
}
4.Floyd-Warshall
Floyd-Warshall使用的是动态规划的思想,不过复杂度比较大,如果对复杂度要求不高的情况下还是很方便的。
代码超时:
01.
02.
#include
03.
#include
04.
#include
05.
using
namespace
std;
06.
07.
const
int
INF=1000000;
08.
int
T,N,M,P,Q;
09.
int
d[1005][1005],g[105];
10.
11.
void
floyd()
12.
{
13.
for
(
int
k=1;k<=M;k++)
14.
for
(
int
i=1;i<=M;i++)
15.
for
(
int
j=1;j<=M;j++)
16.
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
17.
}
18.
19.
int
main()
20.
{
21.
cin>>T;
22.
while
(T--)
23.
{
24.
cin>>N>>M>>P>>Q;
25.
for
(
int
i=0;i<=M;i++)
26.
for
(
int
j=1;j<=M;j++)
27.
if
(i==j)
28.
d[i][j]=0;
29.
else
30.
d[i][j]=INF;
31.
for
(
int
i=0;i
32.
cin>>g[i];
33.
for
(
int
i=0;i
34.
{
35.
int
x,y,z;
36.
cin>>x>>y>>z;
37.
d[x][y]=z;
38.
d[y][x]=z;
39.
}
40.
floyd();
41.
int
res=INF;
42.
for
(
int
i=0;i
43.
if
(res>d[Q][g[i]])
44.
res=d[Q][g[i]];
45.
cout<
46.
}
47.
return
0;
48.
}