usaco2.4.4牛的旅行

76 篇文章 0 订阅

这道题个人觉得很恶心,看了题解发现原来有这么好的方法。。。

一开始用并查集分组,后来发现虽然分组后思路清晰,但很复杂啊!

于是,发现了一种好方法(下次要好好琢磨题目啊):

大体思路是先贮存所有的路径,再用Floyd-Warshall算法,简单来说就是一下算出每对点之间的最短路径,至于具体的内容自己百度。

然后用一个zuiduan数组贮存在未连通时有这个点的最大直径。

再一一枚举每条可以连的路,再判断zuiduan。(这里很神奇的是,连通两个点之后,这个新图形的直径(这里有个要求,这个直径必须经过原来两个图形)就是两个点各自的zuiduan之和再加上这条新加的路径)

选出最小的直径。

再与原先单个图形时的直径进行比较,选出最大的(原先的是必经过两个原图形的,可是也有可能一个原图形的直径就比我们假象的直径要大,也就是那个一个图形的直径才是直径)

最后提醒一下,题目说不要四舍五入,其实是要的。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <iomanip>
using namespace std;
struct aaaa{int x;int y;}s[155];
int n;
double map[155][155],zuiduan[155];
int main()
{
    freopen("cowtour.in","r",stdin);
    freopen("cowtour.out","w",stdout);
     cin>>n;
     for(int i=1;i<=n;i++)
        {cin>>s[i].x>>s[i].y;
         zuiduan[i]=0;
        }
     for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++){
        char a;
        cin>>a;
        if(a=='1'){
            map[i][j]=sqrt((s[i].x-s[j].x)*(s[i].x-s[j].x)+(s[i].y-s[j].y)*(s[i].y-s[j].y));
        }
        else map[i][j]=999999999;
        if(i==j)map[i][j]=0;
     }
     for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++)
     {
         if(map[i][j]>map[i][k]+map[k][j]){
            map[i][j]=map[i][k]+map[j][k];
         }
     }
     for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
     {
         if(map[i][j]!=999999999&&map[i][j]>zuiduan[i])
            zuiduan[i]=map[i][j];
     }
     double aa=999999999;
     for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
     {
         if(map[i][j]==999999999){
            if(aa>zuiduan[i]+zuiduan[j]+sqrt((s[i].x-s[j].x)*(s[i].x-s[j].x)+(s[i].y-s[j].y)*(s[i].y-s[j].y)))
             aa=zuiduan[i]+zuiduan[j]+sqrt((s[i].x-s[j].x)*(s[i].x-s[j].x)+(s[i].y-s[j].y)*(s[i].y-s[j].y));
         }
     }
     for (int i=1;i<=n;i++)
        if (zuiduan[i]>aa) aa=zuiduan[i];
     cout<<setprecision(6)<<fixed;
     cout<<aa<<endl;
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值