Floyd(求最小环+路径) - Sightseeing trip - POJ 1734

Floyd(求最小环) - Sightseeing trip - POJ 1734

题意:

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

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

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

输入格式

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

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

输出格式

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

数据范围

1 ≤ N ≤ 100 , 1 ≤ M ≤ 10000 , 1 ≤ l < 500 1≤N≤100, 1≤M≤10000, 1≤l<500 1N100,1M10000,1l<500

Sample Input:

5 7
1 4 1
1 3 300
3 1 10
1 2 16
2 3 100
2 5 15
5 3 20

Sample Output:

1 3 5 2

分析:

要 求 至 少 包 含 3 个 点 的 环 , 我 们 假 设 环 i − > k − > j − > . . . − > i 。 要求至少包含3个点的环,我们假设环i->k->j->...->i。 3i>k>j>...>i

我 们 知 道 , f l o y d 算 法 每 次 计 算 i 和 j 之 间 , 经 过 k 的 最 短 距 离 。 我们知道,floyd算法每次计算i和j之间,经过k的最短距离。 floydijk

在 第 k 层 循 环 时 , d [ i ] [ j ] 表 示 从 i 到 j , 经 过 的 点 的 编 号 不 超 过 k − 1 , 的 最 短 距 离 。 在第k层循环时,d[i][j]表示从i到j,经过的点的编号不超过k-1,的最短距离。 kd[i][j]ijk1

此 时 前 k − 1 号 点 , 任 意 两 点 之 间 的 最 短 距 离 已 计 算 出 。 此时前k-1号点,任意两点之间的最短距离已计算出。 k1

则 环 的 最 小 长 度 为 g [ i ] [ k ] + g [ k ] [ j ] + d [ j ] [ i ] , 其 中 1 < i , j < k , 这 保 证 了 环 上 无 重 复 点 。 则环的最小长度为g[i][k]+g[k][j]+d[j][i],其中1<i,j<k,这保证了环上无重复点。 g[i][k]+g[k][j]+d[j][i]1<i,jk

用 r e s 更 新 g [ i ] [ k ] + g [ k ] [ j ] + d [ j ] [ i ] 的 最 小 值 , 每 更 新 一 次 都 要 重 新 更 新 一 次 具 体 路 径 。 用res更新g[i][k]+g[k][j]+d[j][i]的最小值,每更新一次都要重新更新一次具体路径。 resg[i][k]+g[k][j]+d[j][i]

求具体路径:

对 于 中 间 点 k , 由 于 从 i 到 k 和 从 k 到 j 的 最 短 路 是 相 互 独 立 的 。 对于中间点k,由于从i到k和从k到j的最短路是相互独立的。 kikkj

对 于 从 j 到 i 的 最 短 路 : 对于从j到i的最短路: ji

我 们 可 以 在 更 新 最 短 路 时 , 用 p o s [ i ] [ j ] = k , 来 记 录 最 短 路 上 的 中 间 点 k , 我们可以在更新最短路时,用pos[i][j]=k,来记录最短路上的中间点k, pos[i][j]=kk

接 着 递 归 求 从 j 到 k 的 最 短 路 经 过 的 点 , 然 后 把 k 加 入 到 路 径 中 , 再 递 归 求 从 k 到 i 的 最 短 路 经 过 的 点 。 接着递归求从j到k的最短路经过的点,然后把k加入到路径中,再递归求从k到i的最短路经过的点。 jkkki

注意:

计 算 环 的 长 度 可 能 会 爆 i n t 。 计算环的长度可能会爆int。 int

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;

const int N=110, inf=0x3f3f3f3f;

int n,m;
int g[N][N],d[N][N];
int pos[N][N];
int path[N],cnt;
int res=inf;

void get_path(int a,int b)	//求从a到b的最短路经过的中间点
{
    if(pos[a][b]==0) return;
    
    int k=pos[a][b];
    get_path(a,k);
    path[cnt++]=k;
    get_path(k,b);
}

void floyd()
{
    memcpy(d,g,sizeof g);
    
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<k;i++)
            for(int j=i+1;j<k;j++)  //i跟j不能重合
                if((long long)g[i][k]+g[k][j]+d[j][i]<res)
                {
                    res=g[i][k]+g[k][j]+d[j][i];
                    cnt=0;
                    path[cnt++]=k;   //从k到i
                    path[cnt++]=i;
                    get_path(i,j);   //从i到j
                    path[cnt++]=j;
                }
                
        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];
                    pos[i][j]=k;
                }
    }
} 

int main()
{
    cin>>n>>m;
    
    memset(g,0x3f,sizeof g);
    for(int i=1;i<=n;i++) g[i][i]=0;

    int a,b,c;
    while(m--)
    {
        cin>>a>>b>>c;
        g[a][b]=g[b][a]=min(g[a][b],c);
    }
    
    floyd();
    
    if(res==inf) puts("No solution.");
    else for(int i=0;i<cnt;i++) cout<<path[i]<<' ';
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值