2021-2022-1 ACM集训队每周程序设计竞赛 - 问题 D: 跑图 - 题解
Posted Tisfy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021-2022-1 ACM集训队每周程序设计竞赛 - 问题 D: 跑图 - 题解相关的知识,希望对你有一定的参考价值。
跑图
时间限制:2秒
空间限制:256M
题目描述
小T给你了一个 N N N点 M M M边的无向带权连通简单图(无向、带权、连通、无自环、无重边)
其中第 i ( 1 ≤ i ≤ M ) i(1\\leq i\\leq M) i(1≤i≤M)条边连接点 a i a_i ai和 b i b_i bi,权重是 c i c_i ci
输出不属于任意两点的最短路径的边的条数。
输入描述
- 2 ≤ N ≤ 100 2\\leq N\\leq 100 2≤N≤100
- N − 1 ≤ M ≤ m i n ( N ( N − 1 ) 2 , 1000 ) N-1\\leq M\\leq min(\\frac{N(N-1)}{2},1000) N−1≤M≤min(2N(N−1),1000)
- 1 ≤ a i , b i ≤ N 1\\leq a_i,b_i\\leq N 1≤ai,bi≤N
- 1 ≤ c i ≤ 1000 1\\leq c_i\\leq 1000 1≤ci≤1000
- c i c_i ci 是整数
- 给定的图没有自环也没有重边
- 给定的图是连通图
输入的格式是:
N M
a1 b1 c1
a2 b2 c2
...
am bm cm
输出描述
输出不属于任意两点的最短路径的边的条数。
即:如果点
a
a
a和点
b
b
b的最短路径经过了边
c
c
c,那么
c
c
c不是答案中的一条。
样例一
输入
3 3
1 2 1
1 3 1
2 3 3
输出
1
提示
- 从点 1 1 1到点 2 2 2,最短路径是 1 → 2 1\\rightarrow2 1→2,权重为 1 1 1
- 从点 2 2 2到点 3 3 3,最短路径是 2 → 3 2\\rightarrow3 2→3,权重为 1 1 1
- 从点 3 3 3到点 1 1 1,最短路径是 3 → 2 → 1 3\\rightarrow2\\rightarrow1 3→2→1,权重为 2 2 2
所以第 3 3 3条边(连接点 1 1 1和点 3 3 3,权重是 3 3 3)不属于上面任何两点之间的最短路径,因此答案是 1 1 1(条)。
题目分析
这题其实是弗洛伊德算法,我们可以算出来任意两点之间的最短路径,遍历给定的每一条边,如果这条边连接的两点的最短距离小于这条边的权重,那么这条边一定不是某个最短路径的一部分(有权重更短的边),累加到结果中即可。
简单描述一下弗洛伊德算法:
定义 P ( i , j , k ) P(i,j,k) P(i,j,k)为:从 v i v_i vi到 v j v_j vj,由序号不大于 k k k的顶点为中间点(或直达)可构成的最短路径。
那么递推公式为 P ( i , j , k ) = m i n ( P ( i , k , k − 1 ) + P ( k , j , k − 1 ) , P ( i , j , k − 1 ) ) P(i,j,k) = min(P(i,k,k-1) + P(k,j,k-1), P(i,j,k-1)) P(i,j,k)=min(P(i,k,k−1)+P(k,j,k−1),P(i,j,k−1))其实我们可以省去一维的空间,用 a [ i ] [ j ] a[i][j] a[i][j]代表从 i i i到 j j j的最短路径。那么如何体现“序号不大于 k k k”呢,只需要第一层循环令 k k k从 1 1 1到 n n n递增即可。
那么递推公式: a [ i ] [ j ] = m i n ( a [ i ] [ j ] , a [ i ] [ k ] + a [ k ] [ j ] ) a[i][j]=min(a[i][j], a[i][k]+a[k][j]) a[i][j]=min(a[i][j],a[i][k]+a[k][j])
AC代码
#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0x3f3f3f3f, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;
int a[111][111];
struct edge
{
int x,y,val;
edge(int a,int b,int c){x=a,y=b,val=c;}
edge(){}
};
edge path[1010];
int main()
{
mem(a); // 初始值是“无穷大”
int n,m;
cin>>n>>m;
fi(i,0,m) // for (int i = 0; i < m; i++)
{
int x,y,val;
cd(x),cd(y),cd(val); // scanf
a[x][y]=a[y][x]=val;
path[i]=edge(x,y,val); // 记录下每一条边
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j||i==k||j==k);
else a[i][j]=min(a[i][j], a[i][k]+a[k][j]);
int ans=0;
for(int i=0;i<m;i++) // 遍历Tisfy给定的每一条边
{
if(a[path[i].x][path[i].y]<path[i].val) // 如果这条边连接的两点的最短路径小于这条边
ans++; // 那么就说明这条边不在最短路径上
}
cout<<ans<<endl;
return 0;
}
原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/120227579
以上是关于2021-2022-1 ACM集训队每周程序设计竞赛 - 问题 D: 跑图 - 题解的主要内容,如果未能解决你的问题,请参考以下文章
BUCT - 2021-2022-1 ACM集训队每周程序设计竞赛(10)题解
BUCT - 2021-2022-1 ACM集训队每周程序设计竞赛题解
BUCT - 2021-2022-1 ACM集训队每周程序设计竞赛题解