P1522 [USACO2.4]牛的旅行 Cow Tours 【Floyd】

题目描述

Farmer John 的农场里有很多牧区。有的路径连接一些特定的牧区。一片所有连通的牧区称为一个牧场。但是就目前而言,你能看到至少有两个牧区通过任何路径都不连通。这样,Farmer John 就有多个牧场了。

John 想在牧场里添加恰好一条路径。对这条路径有以下限制:

一个牧场的直径就是牧场中最远的两个牧区的距离(本题中所提到的所有距离指的都是最短的距离)。考虑如下的有5个牧区的牧场,牧区用 * 表示,路径用直线表示。每一个牧区都有自己的坐标:

            (15,15) (20,15)
             D       E
             *-------*
             |     _/|
             |   _/  |
             | _/    |
             |/      |
    *--------*-------*
    A        B       C
 (10,10)  (15,10) (20,10)

这个牧场的直径大约是 12.0710612.0710612.07106,最远的两个牧区是 A 和 E,它们之间的最短路径是 A→B→EA \to B \to EA→B→E。

这里是另一个牧场:

                     *F(30,15)
                    / 
                  _/  
                _/    
               /      
              *------* 
              G      H
              (25,10)   (30,10)

在目前的情景中,他刚好有两个牧场。John 将会在两个牧场中各选一个牧区,然后用一条路径连起来,使得连通后这个新的更大的牧场有最小的直径。

注意,如果两条路径中途相交,我们不认为它们是连通的。只有两条路径在同一个牧区相交,我们才认为它们是连通的。

输入文件包括牧区、它们各自的坐标,还有一个如下的对称邻接矩阵:


  A B C D E F G H
A 0 1 0 0 0 0 0 0
B 1 0 1 1 1 0 0 0
C 0 1 0 0 1 0 0 0
D 0 1 0 0 1 0 0 0
E 0 1 1 1 0 0 0 0
F 0 0 0 0 0 0 1 0
G 0 0 0 0 0 1 0 1
H 0 0 0 0 0 0 1 0

其他邻接表中可能直接使用行列而不使用字母来表示每一个牧区。输入数据中不包括牧区的名字。

输入文件至少包括两个不连通的牧区。

请编程找出一条连接两个不同牧场的路径,使得连上这条路径后,这个更大的新牧场有最小的直径。输出在所有牧场中最小的可能的直径。
输入格式

第一行一个整数 NNN(1≤N≤1501 \leq N \leq 1501≤N≤150),表示牧区数。

接下来 NNN 行,每行两个整数 X,YX,YX,Y(0≤X,Y≤1050 \leq X ,Y \leq 10^50≤X,Y≤105),表示 NNN 个牧区的坐标。注意每个牧区的坐标都是不一样的。

接下来 NNN 行,每行 NNN 个数字,代表邻接矩阵。
输出格式

只有一行,包括一个实数,表示所求直径。数字保留六位小数。

只需要打到小数点后六位即可,不要做任何特别的四舍五入处理。
输入输出样例
输入 #1

8
10 10
15 10
20 10
15 15
20 15
30 15
25 10
30 10
01000000
10111000
01001000
01001000
01110000
00000010
00000101
00000010

输出 #1

22.071068

说明/提示

翻译来自NOCOW

USACO 2.4

找出每个点与其连通点的最远距离,然后枚举不连通的点。

#include <iostream>
#include <algorithm>
#include <climits>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;

const int inf = 1e9 + 100;
const int maxn = 230;
double a[maxn][maxn];
struct node{
	int x, y;
}point[maxn];
int n;
double d[maxn]; 

double getDis(node x, node y) {
	return sqrt(pow(x.x - y.x, 2) + pow(x.y - y.y, 2));
}

int main() {
	cin >> n;
	fill(a[0], a[0] + maxn * maxn, inf);
	for(int i = 1; i <= n; i++)
		cin >> point[i].x >> point[i].y;
	string str;
	for(int i = 1; i <= n; i++)	a[i][i] = 0;
	for(int i = 1; i <= n; i++) {
		cin >> str;
		for(int j = 0; j < str.size(); j++) {
			if(str[j] == '1') {
				double dis = getDis(point[i], point[j + 1]);
				a[i][j + 1] = a[j + 1][i] = dis;
			}
		}
	}
	for(int k= 1; k <= n; k++)
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= n; j++)
				a[i][j] = min(a[i][j], a[i][k] + a[k][j]);
	double ans1 = 0, ans2 = inf;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			if(a[i][j] != inf)
				d[i] = max(d[i], a[i][j]), ans1 = max(ans1, d[i]);
		}
	}
	for(int i = 1; i <= n; i++) {
		for(int j = i + 1; j <= n; j++) {
			if(a[i][j] == inf)
				ans2 = min(ans2, getDis(point[i], point[j]) + d[i] + d[j]);
		}
	}
	printf("%.6lf", max(ans1, ans2));
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值