POJ 1734 Sightseeing trip


题目大意:
在一张无向图上求一个不是自环的最小环


分析:
好难呀,如果只求最小环的权值我还勉强可以写出来,但是加上记录路径就完全不会了,觉得自己弱爆了O(≧口≦)O
先说怎么求最小环:
我们考虑这样一个问题,怎么求最短路?很简单Floyd、dijkstra,etc.,那么最小环呢?所以我们就要先考虑环和路径之间有什么关系,很明显,环是由路径连接而成的,详细来说,一个i~i的环可以拆成三条边i~k的直接路径,k~j的直接路径以及i~j的最短路径。为什么要这么拆呢?为什么不拆成两条最短路径或者三条最短路径呢?这就是Floyd的功劳了,我们考虑Floyd是怎么求最短路的,dis[i][j]=min(dis[i][k]+dis[k][j]),我们是通过枚举ij之间的中间点k来更新ij之间的最短路径,所以我们也可以通过枚举点k来更新ii之间的最短路径,也就是环,但是直接更新肯定不行,因为我们无法保证我们求出的ij之间的最短路不包含k,所以怎么办呢?我们可以强制要求k是环上编号最大的点,然后通过枚举k来更新最小环。
接下来就是很难想到的路径记录问题了:我们可以发现,一条从i到j的路径,如果看成有向边的话,每个点都有在这条路径上的唯一father节点,有了这个想法,这个问题就很好解决了,我们记录一下ij之间的最短路径上每个节点的pre值,在更新dis的时候,更新pre


代码如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 0x7ffffff//注意inf不能开太小会WA太大会RE
using namespace std;
const int maxn=100+5;
int mp[maxn][maxn],n,m,path[maxn],cnt,pre[maxn][maxn],dis[maxn][maxn],ans;
inline int read(){
    char ch=getchar();
    int f=1,x=0;
    while(!(ch>='0'&&ch<='9')){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
        x=x*10+ch-'0',ch=getchar();
    return f*x;
}
signed main(void){
    while(scanf("%d%d",&n,&m)!=EOF){
        cnt=0,ans=inf;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                pre[i][j]=i,mp[i][j]=dis[i][j]=inf;
        for(int i=1,x,y,z;i<=m;i++)
            x=read(),y=read(),z=read(),dis[x][y]=dis[y][x]=mp[x][y]=mp[y][x]=min(mp[x][y],z);
        for(int k=1;k<=n;k++){
            for(int i=1;i<k;i++)
                for(int j=i+1;j<k;j++)
                    if(ans>mp[i][k]+mp[k][j]+dis[i][j]){
                        ans=mp[i][k]+mp[k][j]+dis[i][j];//dis--最短距离,mp---直接距离 
                        cnt=0;
                        int tmp=j;
                        while(tmp!=i){
                            path[++cnt]=tmp;
                            tmp=pre[i][tmp];                        
                        }
                        path[++cnt]=i,path[++cnt]=k;
                    }
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    if(dis[i][j]>dis[i][k]+dis[k][j]){
                        dis[i][j]=dis[i][k]+dis[k][j];
                        pre[i][j]=pre[k][j];
                    }
        }
        if(ans==inf)
            printf("No solution.\n");
        else{
            for(int i=1;i<=cnt;i++)
                cout<<path[i]<<" ";
            cout<<endl;
        }
    }
    return 0;
}

by >o< neighthorn

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值