D110【模板】最短路→最小环 Floyd 算法 P10927 Sightseeing trip

D110【模板】最短路→最小环 Floyd 算法 P10927 Sightseeing trip_哔哩哔哩_bilibili

 

P10927 Sightseeing trip - 洛谷

给一个无向图,输出最小环上的点。

最小环 - OI Wiki

Floyd 算法在最外层循环到点 $k$ 时(尚未更新 $d$ 时),$d_{i,j}$ 是从 $i$ 到 $j$ 且仅经过编号 $[1,k-1]$ 中的点的最短路

当前最小环中编号最大的顶点为 $k$,环上与 $k$ 相邻的两个点为 $i,j$,该环的长度为 $d_{i,j}+w_{i,k}+w_{j,k}$.枚举 $k$ 更新最小环

更新最短路时,用数组 $p[i][j]=k$ 记录从 $i$ 到 $j$ 的最短路经过 $k$ 点

更新最小环时,用数组 path[cnt] 记录 $i,k,j$ 三点,再递归获得 $j$ 到 $i$ 的最短路上的中间点

image image 

相关板子:

D04【模板】最短路 Floyd 算法 - 董晓 - 博客园

 

// 最短路→最小环 Floyd 算法 O(N^3)
#include<bits/stdc++.h>
using namespace std;

const int N=110,INF=0x3f3f3f3f;
int n,m,cnt,res=INF;
int w[N][N],d[N][N];
int p[N][N],path[N];

void get_path(int i,int j){ //递归 i,j 之间的点
  if(p[i][j]==0) return;
  int k=p[i][j];
  get_path(i,k);
  path[++cnt]=k;
  get_path(k,j);
}
void Floyd(){
  for(int k=1; k<=n; k++){
    for(int i=1;i<k;i++)
    for(int j=i+1;j<k;j++)
    if(res>1ll*d[i][j]+w[i][k]+w[j][k]){
      res=d[i][j]+w[i][k]+w[j][k]; //更新最小环
      cnt=0;
      path[++cnt]=i; path[++cnt]=k; path[++cnt]=j;
      get_path(j,i); //获取j到i的最短路上的中间点
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    if(d[i][j]>d[i][k]+d[k][j]){
      d[i][j]=d[i][k]+d[k][j]; //更新最短路
      p[i][j]=k; //记录从i到j的最短路经过k点
    }
  }  
}
int main(){
  cin>>n>>m;
  memset(w,0x3f,sizeof w);
  for(int i=1;i<=n;i++) w[i][i]=0; //去自环
  for(int a,b,c;m--;){
    cin>>a>>b>>c;
    w[a][b]=w[b][a]=min(w[a][b],c); //存边权,去重边
  }
  
  memcpy(d,w,sizeof d);
  Floyd();
  if(res==INF) puts("No solution.");
  else for(int i=1;i<=cnt;i++) cout<<path[i]<<' ';
}

 

posted @ 2026-03-21 11:33  董晓  阅读(128)  评论(0)    收藏  举报