图论习题1

[刷题记录]图论习题1(floyd应用)

  • 搞了三道图论入门题,全是$floyd$,记录一下。

洛谷 P1364 医院设置

题意

  • 有一棵二叉树,每个节点代表一个地方的人数,相邻节点之间距离为1,现在要在某个节点设置医院,要求所有人到医院的路程之和最小,求最小路程和。

Sample Input

1
2
3
4
5
6
5	//5个节点					
13 2 3 //第i个节点的人数 左儿子是2 右儿子是3
4 0 0 // 0代表没有这个儿子
12 4 5
20 0 0
40 0 0

Sample Output

1
81

思路&题解

  • 先$floyd$预处理出每两个节点之间的距离,然后遍历每一个节点,在该节点上设置医院并求出路程和,更新最小路程和。

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
#include<bits/stdc++.h>
using namespace std;
int dp[105][105],num[105];
int ans=1e9;
int n;
void gao(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]);
}
}
}
}

int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) continue;
else dp[i][j]=1e9;
}
}
for(int i=1;i<=n;i++){
cin>>num[i];
int x;
cin>>x;
if(x!=0){
dp[i][x]=1;
dp[x][i]=1;
}
cin>>x;
if(x!=0){
dp[i][x]=1;
dp[x][i]=1;
}
}
gao();
for(int k=1;k<=n;k++){
int temp = 0;
for(int i=1;i<=n;i++){
if(i==k) continue;
temp+=dp[k][i]*num[i];
}
ans = min(ans,temp);
}
cout<<ans<<endl;
return 0;
}//https://www.luogu.com.cn/problem/P1364

vijos p1446 最短路上的统计

题意

  • 一个无向无环图,边权都为1,有$n$次询问,每次询问一个点对$(a,b)$,要求输出$a$与$b$之间所有最短路上的点的总个数(注意:可能有多条最短路)。

思路&题解

  • $floyd$过后,针对每次询问,开始遍历所有点作为中间节点,判断是否有$dp[a][b] == dp[a][i]+dp[i][b]$即可。

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
#include<bits/stdc++.h>
using namespace std;
int dp[105][105];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) dp[i][j]=0;
else dp[i][j]=1e9;
}
}
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
dp[x][y]=dp[y][x]=1;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]);
}
}
int p;
cin>>p;
for(int i=1;i<=p;i++){
int x,y;
cin>>x>>y;
int cnt=0;
for(int i=1;i<=n;i++){
if(dp[x][y]==dp[x][i]+dp[i][y])
cnt++;
}
cout<<cnt<<endl;
}
return 0;
}//https://vijos.org/p/1446

vijos p1046 观光旅游

题意

  • 解释过来就是:有一个无向无环有权图(可能有重边),求图中的最小环。(注意:无向图的最小环最小为3个节点组成的环)

思路&题解

  • 经典的无向图最小环问题,思路是:在$floyd$求解过程中,在枚举到$k$节点为中间节点时,所有点以第$1…k-1$的作为中间节点的最短路都已经算好了,它们都是不经过$k$节点的最短路,因此可以利用这一点,在更新本次以$k$节点为中间节点的最短路之前,枚举$k$节点之前的所有节点来求最小环大小,状态转移方程:其中,$dis[i][j]$为更新过的$i$到$j$的最短路,$mm$数组为原始图的邻接矩阵。

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
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[105][105];
ll n,m;
ll ans[105][105];
ll anss=1e9;

void floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<k;i++){
for(int j=i+1;j<k;j++){ //核心代码
anss = min(anss,dp[i][j]+ans[i][k]+ans[k][j]);
}
}
for(int i=1;i<=n;i++){ //floyd
for(int j=1;j<=n;j++){
dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]);
}
}
}
}
int main(){
while(cin>>n>>m){
anss=1e9;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i!=j) ans[i][j]=dp[i][j]=1e9;
}
}
while(m--){
ll x,y,d;
cin>>x>>y>>d;
dp[x][y]=dp[y][x]=ans[x][y]=ans[y][x]=min(dp[x][y],d);
}
floyd();
if(anss==1e9) cout<<"No solution."<<endl;
else cout<<anss<<endl;
}
return 0;
} //https://vijos.org/p/1046

  • $floyd$代码还是比较好理解的,主要是最小环算法比较巧妙orz,继续加油!