【AcWing 344】 最小环问题 floyd

给定一张无向图,求图中一个至少包含3个点的环,环上的节点不重复,并且环上的边的长度之和最小。

该问题称为无向图的最小环问题。

你需要输出最小环的方案,若最小环不唯一,输出任意一个均可。

输入格式
第一行包含两个整数N和M,表示无向图有N个点,M条边。

接下来M行,每行包含三个整数u,v,l,表示点u和点v之间有一条边,边长为l。

输出格式
输出占一行,包含最小环的所有节点(按顺序输出),如果不存在则输出’No solution.’。

数据范围
1≤N≤100,
1≤M≤10000,
1≤l<500
输入样例:
5 7
1 4 1
1 3 300
3 1 10
1 2 16
2 3 100
2 5 15
5 3 20
输出样例:
1 3 5 2

代码和思路来自大佬@spnooyseed的题解
https://www.acwing.com/solution/acwing/content/4494/

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 410 ;
const int INF = 0x3f3f3f3f ;
typedef long long ll ;
int a[N][N] , dis[N][N] ; // a 储存 两点之间的距离   dis 储存两点之间的最短路径
int edge[N] ;  // edge 存储最小环的路径
int ne[N][N] ; // ne[i][j] 表示从i点到j点这个总的路径上面(很多条边) , i 点的下一个点是什么
int main()
{
  //  freopen("DATA.txt","r",stdin);
    int n , m ;
    scanf("%d%d" , &n , &m) ;
    memset(a , 0x3f , sizeof a) ;
    memset(dis , 0x3f , sizeof dis) ;
    while(m --)
    {
        int x , y ,z ;
        scanf("%d%d%d", &x , &y , &z) ;
        a[x][y] = a[y][x] = dis[x][y] = dis[y][x] = min(dis[x][y] , z) ; // 这个就是怕有重边的,先筛选一下
        ne[x][y] = y , ne[y][x] = x ;
        // 刚开始以为x和y之间肯定只有一条边,然后x——y路径 x的下一个点就是y ,y的下一个点就是x
    }
    ll ans = INF ;
    int cnt ;
    //下面的大体框架就是floyd算法, 三个循环,
    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(ans >0ll +  dis[i][j] + a[i][k] + a[k][j])  // 这个地方就是判断最小环的地方,如果存在更小的环就处理一下
             {
                 ans =0ll +  dis[i][j] + a[i][k] + a[k][j] ;
                 cnt = 0 ;
                 for(int p = i ;p != j ;p = ne[p][j]) // 这个地方就是将i j 两点 组成的很多路径 和最后的一个点k插入edge数组
                 // p = ne[p][j] ,这个的意思就是p-j路径p的下一个点 ,p从i开始
                  edge[++ cnt] = p ;
                 edge[++ cnt] = j , edge[++ cnt] = k ; // 因为 k肯定不在i-j这个最短路径上面,然后单独将其加入
                 // 因为上面的一个循环到p == j 就停止了,也就是说j这个点并没有被插入,所以也突然插一下
             }
         for(int i = 1 ;i <= n ;i ++)
          for(int j = 1 ;j <= n ;j ++)
           if(dis[i][j] > dis[i][k] + dis[k][j])  // 这个就是floyd主算法
            {
                dis[i][j] = dis[i][k] + dis[k][j] ;
                ne[i][j] = ne[i][k] ; // 额外加了一个这个 ,因为i--j 已经是最短了
               // ,此时此刻i-j路径上面只有三个点,因为是从ijk三个点转移过来的 , 所以 i 的下一个点肯定是k
            }
     }
     // 最后就处理一下就行了

    if(ans == INF )
     cout << "No solution." << endl ;
    else
    {
        for(int i = 1 ;i <= cnt ;i ++)
         cout << edge[i] << " " ;
        cout << endl ;
    }
    return 0 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值