图论-最短路专题

最短路专题

DJ裸搜算法 DJ+优化队列算法 spfa(BF+队列)算法 floyd算法

POJ 2387

Problem Description

Input

Output

Sample Input

Sample Output

题目要求

最短路模板题,可能存在重边。

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<iostream>
#include<climits>
using namespace std;
int t,n;
int map[2020][2020];
int dis[2020],vis[2020];
void DJ(int s)
{
for(int i=1;i<=n;i++)
dis[i]=map[s][i];
memset(vis,0,sizeof(vis));
vis[s]=1;
dis[s]=0;
int min,next;
for(int i=0;i<n;i++)
{
min=INT_MAX;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&min>dis[j])
{
min=dis[j];
next=j;
}
}
if(min==INT_MAX)
break;
vis[next]=1;
for(int j=1;j<=n;j++)
if(!vis[j]&&map[next][j]!=INT_MAX&&dis[next]+map[next][j]<dis[j])
dis[j]=map[next][j]+dis[next];
}
}
int main()
{
while(cin>>t>>n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j)
map[i][j]=0;
else
map[i][j]=INT_MAX;
for(int i=0;i<t;i++)
{
int u,v,w;
cin>>u>>v>>w;
if(w<map[u][v])
map[u][v]=map[v][u]=w;
}
DJ(1);
cout<<dis[n]<<endl;
}
return 0;
}

思路

DJ算法裸搜。
注意在数组map存图的时候要多判断一个w<map[u][v],因为有重边的情况需要存在最小的权值。

HDU 2544

Problem Description

Input

Output

Sample Input

Sample Output

参考AC代码(一):DJ裸搜

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<iostream>
#include<cstring>
#include<climits>
using namespace std;
int n,m;
int map[105][105];
int vis[10050],dis[10050];
void DJ(int s)
{
for(int i=1;i<=n;i++)
dis[i]=map[s][i];
memset(vis,0,sizeof(vis));
vis[s]=1;
dis[s]=0;
int min,next;
for(int i=0;i<n;i++)
{
min=INT_MAX;
for(int j=1;j<=n;j++)
{
if(dis[j]<min&&!vis[j])
{
min=dis[j];
next=j;
}
}
if(min==INT_MAX)
break;
vis[next]=1;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&map[next][j]!=INT_MAX&&dis[next]+map[next][j]<dis[j])
dis[j]=dis[next]+map[next][j];
}
}
}
int main()
{
while(cin>>n>>m)
{
if(n==0&m==0)
break;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j)
map[i][j]=0;
else
map[i][j]=INT_MAX;
for(int i=0;i<m;i++)
{
int u,v,w;
cin>>u>>v>>w;
map[u][v]=map[v][u]=w;
}
DJ(1);
cout<<dis[n]<<endl;
}
return 0;
}

参考AC代码(二):DJ优化(priority_queue)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<climits>
using namespace std;
int n,m;
int dis[10050];
struct node
{
int x,id;
node(int u,int w){x=u,id=w;}
bool operator < (const node &a) const
{
return id>a.id;
}
};
vector<node>eg[10050];
void DJ(int s)
{
for(int i=1;i<=n;i++)
dis[i]=INT_MAX;
dis[s]=0;
priority_queue<node>q;
q.push(node(s,dis[s]));
while(!q.empty())
{
node now=q.top();
q.pop();
for(int i=0;i<eg[now.x].size();i++)
{
node next=eg[now.x][i];
if(dis[next.x]>dis[now.x]+next.id)
{
dis[next.x]=now.id+next.id;
q.push(node(next.x,dis[next.x]));
}
}
}
}
int main()
{
while(cin>>n>>m)
{
if(n==0&&m==0)
break;
for(int i=1;i<=n;i++)
eg[i].clear();
for(int i=0;i<m;i++)
{
int u,v,w;
cin>>u>>v>>w;
eg[u].push_back(node(v,w));
eg[v].push_back(node(u,w));
}
DJ(1);
cout<<dis[n]<<endl;
}
return 0;
}

参考AC代码(三):spfa优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#include<climits>
using namespace std;
int vis[10050],dis[10050];
int n,m;
struct node
{
int x,id;
node(int u,int w){x=u,id=w;}
};
vector<node>eg[10050];
void spfa(int s)
{
vis[s]=1;
for(int i=1;i<=n;i++)
dis[i]=INT_MAX;
dis[s]=0;
queue<int>q;
q.push(s);
while(!q.empty())
{
int now=q.front();
q.pop();
vis[now]=0;
for(int i=0;i<eg[now].size();i++)
{
if(eg[now][i].id+dis[now]<dis[eg[now][i].x])
{
dis[eg[now][i].x]=eg[now][i].id+dis[now];
if(!vis[eg[now][i].x])
{
q.push(eg[now][i].x);
vis[eg[now][i].x]=1;
}
}
}
}
}
int main()
{
while(cin>>n>>m)
{
if(n==0&&m==0)
break;
for(int i=1;i<=n;i++)
eg[i].clear();
memset(vis,0,sizeof(vis));
while(m--)
{
int u,v,w;
cin>>u>>v>>w;
eg[u].push_back(node(v,w));
eg[v].push_back(node(u,w));
}
spfa(1);
cout<<dis[n]<<endl;
}
return 0;
}

参考AC代码(四):floyd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<iostream>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
int n,m;
int f[10050][10050];
int main()
{
while(cin>>n>>m)
{
if(n==0&&m==0)
break;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j)
f[i][j]=0;
else
f[i][j]=INT_MAX;
while(m--)
{
int u,v,w;
cin>>u>>v>>w;
f[u][v]=f[v][u]=w;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
cout<<f[1][n]<<endl;
}
return 0;
}

思路

最短路水题。
四种代码分别使用了:DJ裸搜算法,DJ优化算法,spfa算法,floyd算法。

UVa 247

Problem Description
如果两个人相互打电话(直接或间接),则说他们在同一个电话圈里。例如,a打给b,b打给c,c打给d,d打给a,则这四
个人在同一个圈里;如果e打给f但f不打给e,则不能推出e和f在同一个电话圈里。输入n(n<=25)个人的m次电话,找出所
有电话圈。人名只包含字母,不超过25个字符,且不重复。

Input
The input file will contain one or more data sets. Each data set begins with a line containing two
integers, n and m. The first integer, n, represents the number of different people who are in the data
set. The maximum value for n is 25. The remainder of the data set consists of m lines, each representing
a phone call. Each call is represented by two names, separated by a single space. Names are first names
only (unique within a data set), are case sensitive, and consist of only alphabetic characters; no name
is longer than 25 letters.
For example, if Ben called Dolly, it would be represented in the data file as
Ben Dolly
Input is terminated by values of zero (0) for n and m.

Output
For each input set, print a header line with the data set number, followed by a line for each calling
circle in that data set. Each calling circle line contains the names of all the people in any order within
the circle, separated by comma-space (a comma followed by a space). Output sets are separated by
blank lines.

Sample Input
5 6
Ben Alexander
Alexander Dolly
Dolly Ben
Dolly Benedict
Benedict Dolly
Alexander Aaron
14 34
John Aaron
Aaron Benedict
Betsy John
Betsy Ringo
Ringo Dolly
Benedict Paul
John Betsy
John Aaron
Benedict George
Dolly Ringo
Paul Martha
George Ben
Alexander George
Betsy Ringo
Alexander Stephen
Martha Stephen
Benedict Alexander
Stephen Paul
Betsy Ringo
Quincy Martha
Ben Patrick
Betsy Ringo
Patrick Stephen
Paul Alexander
Patrick Ben
Stephen Quincy
Ringo Betsy
Betsy Benedict
Betsy Benedict
Betsy Benedict
Betsy Benedict
Betsy Benedict
Betsy Benedict
Quincy Martha
0 0

Sample Output
Calling circles for data set 1:
Ben, Alexander, Dolly, Benedict
Aaron
Calling circles for data set 2:
John, Betsy, Ringo, Dolly
Aaron
Benedict
Paul, George, Martha, Ben, Alexander, Stephen, Quincy, Patrick

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include<iostream>
#include<map>
#include<vector>
#include<string.h>
#include<string>
#include<algorithm>
using namespace std;
int vis[30],f[30][30];
map<string,int>name;
vector<string>Name;
int flag=1;
int n,m;
void dfs(int u)
{
vis[u]=1;
for(int i=0;i<n;i++)
if(f[u][i]&&f[i][u])
if(!vis[i])
{
cout<<", "<<Name[i];
dfs(i);
}
}
int main()
{
while(cin>>n>>m)
{
if(n==0&&m==0)
break;
name.clear();
Name.clear();
memset(f,0,sizeof(f));
memset(vis,0,sizeof(vis));
int id=0;
for(int i=0;i<m;i++)
{
string a,b;
cin>>a>>b;
if(!name.count(a))
{
name[a]=id++;
Name.push_back(a);
}
if(!name.count(b))
{
name[b]=id++;
Name.push_back(b);
}
int x=name[a],y=name[b];
f[x][y]=1;
}
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
f[i][j]=(f[i][j]||(f[i][k]&&f[k][j]));
if(flag>1)
cout<<endl;
cout<<"Calling circles for data set " << flag++ <<":" << endl;
for(int i=0;i<n;i++)
{
if(!vis[i])
{
cout<<Name[i];
dfs(i);
cout<<endl;
}
}
}
return 0;
}

思路

首先用floyd求出传递闭包,即g[i][j]表示i是否直接或者间接给j打过电话,当且仅当g[i][j]=g[j][i]=1时二者处于一个电话圈。
构造一个新图,在一个电话圈里的两个人之间连一条边,然后依次输出各个连通分量的所有人即可。
注意此题的floyd需稍加改变,因为本题判断的是连通性而非求出最短路。权值为0或1。

UVa 10048

Problem Description
输入一个C个点S条边(c<=100,s<=1000)的无向带权图,边权表示该路径上的噪声值。当噪声值太大时,耳膜可能会受到
伤害,所以当你从某点去往另一个点时,总是希望路上经过的最大噪声值最小。输入一些询问,每次询问两个点,输出两
点间最大噪声值最小的路径。

Input
The input may contain multiple test cases.
The first line of each test case contains three integers C(≤ 100), S(≤ 1000) and Q(≤ 10000) where
C indicates the number of crossings (crossings are numbered using distinct integers ranging from 1 to
C), S represents the number of streets and Q is the number of queries.
Each of the next S lines contains three integers: c1, c2 and d indicating that the average sound
intensity level on the street connecting the crossings c1 and c2 (c1 ̸= c2) is d decibels.
Each of the next Q lines contains two integers c1 and c2 (c1 ̸= c2) asking for the minimum sound
intensity level you must be able to tolerate in order to get from crossing c1 to crossing c2.
The input will terminate with three zeros form C, S and Q.

Output
For each test case in the input first output the test case number (starting from 1) as shown in the
sample output. Then for each query in the input print a line giving the minimum sound intensity level
(in decibels) you must be able to tolerate in order to get from the first to the second crossing in the
query. If there exists no path between them just print the line “no path”.
Print a blank line between two consecutive test cases.

Sample Input
7 9 3
1 2 50
1 3 60
2 4 120
2 5 90
3 6 50
4 6 80
4 7 70
5 7 40
6 7 140
1 7
2 6
6 2
7 6 3
1 2 50
1 3 60
2 4 120
3 6 50
4 6 80
5 7 40
7 5
1 7
2 4
0 0 0

Sample Output
Case #1
80
60
60
Case #2
40
no path
80

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<iostream>
#include<cstring>
#include<algorithm>
#define INF 0x7FFFFFF
using namespace std;
int f[105][105];
int n,m,q;
int flag=1;
int main()
{
while(cin>>n>>m>>q) //n:点 m:边 q:询问次数
{
if(n==0&&m==0&&q==0)
break;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(j==i)
f[i][j]=0;
else
f[i][j]=INF;
}
for(int i=0;i<m;i++)
{
int u,v,w;
cin>>u>>v>>w;
f[u][v]=f[v][u]=w;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],max(f[i][k],f[k][j]));
if(flag!=1)
cout<<endl;
cout<<"Case #"<<flag++<<endl;
for(int i=0;i<q;i++)
{
int u,v;
cin>>u>>v;
if(f[u][v]!=INF)
cout<<f[u][v]<<endl;
else
cout<<"no path"<<endl;
}
}
return 0;
}

思路

输出两点之间权值最大的最短路径。
floyd需稍加修改:f[i][j]=min(f[i][j],max(f[i][k],f[k][j]));

文章目录
  1. 1. 最短路专题
    1. 1.1. POJ 2387
      1. 1.1.1. 题目要求
      2. 1.1.2. 参考AC代码
      3. 1.1.3. 思路
    2. 1.2. HDU 2544
      1. 1.2.1. 参考AC代码(一):DJ裸搜
      2. 1.2.2. 参考AC代码(二):DJ优化(priority_queue)
      3. 1.2.3. 参考AC代码(三):spfa优化
      4. 1.2.4. 参考AC代码(四):floyd
      5. 1.2.5. 思路
    3. 1.3. UVa 247
      1. 1.3.1. 参考AC代码
      2. 1.3.2. 思路
    4. 1.4. UVa 10048
      1. 1.4.1. 参考AC代码
      2. 1.4.2. 思路
|