hdu1599 find the mincost route (无向图最小环问题)

Problem Description

杭州有N个景区,景区之间有一些双向的路来连接,现在8600想找一条旅游路线,这个路线从A点出发并且最后回到A点,假设经过的路线为V1,V2,…VK,V1,那么必须满足K>2,就是说至除了出发点以外至少要经过2个其他不同的景区,而且不能重复经过同一个景区。现在8600需要你帮他找一条这样的路线,并且花费越少越好。

Input

第一行是2个整数N和M(N <= 100, M <= 1000),代表景区的个数和道路的条数。
接下来的M行里,每行包括3个整数a,b,c.代表a和b之间有一条通路,并且需要花费c元(c <= 100)

Output

对于每个测试实例,如果能找到这样一条路线的话,输出花费的最小值。如果找不到的话,输出"It’s impossible.".

Sample Input

3 3
1 2 1
2 3 1
1 3 1
3 3
1 2 1
1 2 3
2 3 1

Sample Output

3
It’s impossible.

分析:

最小环是指在一个图中,有n个节点构成的边权和最小的环。
有向图的最小环至少由两个节点组成。
无向图的最小环至少由三个节点组成。(因为无向图道路是双向的)

解法1:

多次dijkstra

任意最小环,肯定有直接相连的两点,假设是i和j
则最小环由这两点的直接边加上经过其他点的路径组成
先把i和j直接相连的边删除(这里我假设已经消去重边,即取最小边)
再求一次最短路(这时候的最短路一定是经过其他点的)
这样i、j所在最小环就是i和j之间直接相连的最小边加上刚刚求出的i到j的最短路距离
但是这样只求出i、j所在环,可能还有其他更小的环,所以要多找几对点跑最短路找答案
(其实就是枚举删除所有边,然后用边的两个端点i、j尝试更新答案)

解法2:

(先说明一下,这个解法里面inf不能设置的太大,因为floyd需要三个inf相加,三个0x3f3f3f3f会炸int)

floyd判断最小环(只适用于点少的情况)

floyd最外层的k循环就是枚举i到j中间经过的点来尝试更新最短路
当到第k个点的时候,意味着k点之前的点已经完成了对最短路的更新
到点k的时候,我们先不更新最短路,这样i到j之间的最短路上的点一定小于k
这时候i,j,k三点是不同的三个点
这个环的最短长度为g(i,k)+g(k,j)+i到j的路径中节点小于k的最短路d(i,j)
其中d(i,j)表示两点的最短路(路径中节点小于k),g(i,j)表示直接相连的边(不需要更新)

解法3:

最小生成树+LCA

最小环一定是最小生成树边加上树外加入某边后形成的环,
因此可以枚举树外每一条边,假设边端点为u,v
则环的权值为树中u到v的距离加上边的权值
树中u到v的距离可以用LCA解决

code(floyd):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#include<sstream>
#include<stack>
#define ll long long
#define s0(a) memset((a),0,sizeof(a))
//void fp(){freopen("1.txt","r",stdin);freopen("2.txt","w",stdout);}
//void ddd(){int x=10;while(x--)putchar('-');puts("");};
const int inf=0x3f3f3f3f;
const int inn=-(1<<30);
using namespace std;
const int maxm=105;
ll g[maxm][maxm];//存原图(不更新)
ll d[maxm][maxm];//存最短路的图(更新)
void init(int n){//初始化
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            g[i][j]=d[i][j]=inf;
        }
    }
}
int main(){
//    ios::sync_with_stdio(0);
    int n,m;
    while(cin>>n>>m){
        init(n);
        for(int i=1;i<=m;i++){
            ll a,b,c;
            cin>>a>>b>>c;
            d[a][b]=d[b][a]=g[a][b]=g[b][a]=min(g[a][b],c);//加min消去重边
        }
        ll ans=inf;
        for(int k=1;k<=n;k++){
            for(int i=1;i<=k-1;i++){//这里只循环到k-1
                for(int j=i+1;j<=k-1;j++){
                	ans=min(ans,d[i][j]+g[i][k]+g[k][j]);//更新答案
                }
            }
            for(int i=1;i<=n;i++){//更新最短路,循环全部点
                for(int j=1;j<=n;j++){
                    d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
                }
            }
        }
        if(ans==inf){
            cout<<"It's impossible."<<endl;
        }else{
            cout<<ans<<endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值