【题目链接】
【解题思路】
题目中说了,至少要有三点才能算一个环,那么我们其实只需要枚举这三个点再将连接他们几个的三条边加起来就可以了。
如果做完了Floyd再跑一次枚举三个点,我们会发现时间复杂度会慢一点,所以我们考虑进行一些优化,一边Floyd,一边求最小环,这样我们的时间复杂度会快一点。
证明:当我们以 k k k 为中转点的时候,我们 1 ∼ k − 1 1\sim k-1 1∼k−1 的点一定是处理过的并且结果为最优,那么我们就以 k k k 为一个点,再在 1 ∼ k − 1 1\sim k-1 1∼k−1 枚举两个点就可以啦。
解释一下三边:首先定义三个点, i , j , k i,j,k i,j,k, i , j i,j i,j 为与 k k k 相邻的两个点。
- k k k 与 i i i 连接的边。
- k k k 与 i i i 连接的边。
- i i i 与 j j j 连接的边。
证明:
k
和
i
之
间
的
做
短
路
或
k
和
j
之
间
的
做
短
路
可
能
会
有
一
些
重
叠
,
这
样
就
不
是
一
个
环
啦
,
所
以
k
必
须
与
i
,
j
相
邻
k和i之间的做短路或k和j之间的做短路可能会有一些重叠,这样就不是一个环啦,所以k必须与i,j相邻
k和i之间的做短路或k和j之间的做短路可能会有一些重叠,这样就不是一个环啦,所以k必须与i,j相邻
Tips:我们要注意初始化,如果太大三边之和会爆int,当有重边是选择最小的一条。
【CODE】
#include<bits/stdc++.h>
using namespace std;
int n,m,u,v,d,minn;
int mp[111][111];
int dis[111][111];
int main()
{
cin>>n>>m;
minn=10000000;
memset(dis,0x3f/2,sizeof(dis));
memset(mp,0x3f/2,sizeof(mp));
for (int i=1;i<=m;i++)
{
cin>>u>>v>>d;
dis[u][v]=mp[u][v]=min(mp[u][v],d);
dis[v][u]=mp[v][u]=min(mp[v][u],d);
}
for (int k=1;k<=n;k++)
{
for (int i=1;i<=k-1;i++)
for (int j=i+1;j<=k-1;j++)//提高效率,前面的点都枚举过啦
if (dis[i][j]+mp[k][i]+mp[k][j]<minn)//三条边之和
minn=dis[i][j]+mp[k][i]+mp[k][j];
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (dis[i][j]>dis[k][i]+dis[k][j])
dis[i][j]=dis[k][i]+dis[k][j];
}
if (minn==10000000)
cout<<"No solution.";
else cout<<minn;
return 0;
}