【最短路】【圖論】【Floyed】牛的旅行(ssl 1119/luogu 1522)

牛的旅行

ssl 1119

luogu 1522

題目大意

有兩堆點,每一堆點之中的任何兩個點都一定有相連的路線,連接兩堆點中的各一個點,使最遠的兩個點的距離最短


原題

農民John的農場裏有很多牧區。有的路徑連接一些特定的牧區。一片所有連通的牧區稱爲一個牧場。但是就目前而言,你能看到至少有兩個牧區不連通。這樣,農民John就有多個牧區了。

John想在農場裏添加一條路徑(注意,恰好一條)。對這條路徑有以下限制:

一個牧場的直徑就是牧場中最遠的兩個牧區的距離(本題中所提到的所有距離指的都是最短的距離)。考慮如下的有5個牧區的牧場,牧區用星號表示,路徑用直線表示。每一個牧區都有自己的座標:

在這裏插入圖片描述

這個牧場的直徑大約是12.07106,最遠的兩個牧區是A和E,它們之間的最短路徑是A-B-E。

這裏是另一個牧場:

在這裏插入圖片描述

這兩個牧場都在John的農場上。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

輸入文件至少包括兩個不連通的牧區。

請編程找出一條連接兩個不同牧場的路徑,使得連上這條路徑後,這個更大的新牧場有最小的直徑。

Input

第1行: 一個整數N (1 <= N <= 150), 表示牧區數

第2到N+1行: 每行兩個整數X,Y (0 <= X ,Y<= 100000), 表示N個牧區的座標。注意每個 牧區的座標都是不一樣的。

第N+2行到第2*N+1行: 每行包括N個數字(0或1) 表示如上文描述的對稱鄰接矩陣。

Output

只有一行,包括一個實數,表示所求答案。數字保留六位小數。

Sample Input

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

Sample Output

22.071068

解題方法:

先求出兩個區塊的最短路,再求出每個區塊中每個點與相距此點最大的點的距離,枚舉兩邊的所有點,將任意兩個點(不同區域)連接後的的結果就是兩個點的距離加上兩個點在本區域的相差最大的距離(若不懂,看代碼)

注意:

當出現下圖時(推薦按住左鍵再移動來放大圖片),要連接3和4,但最大長度不是1-3-4或2-3-4,而是1-2,所以要注意

在這裏插入圖片描述

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
int n;
double f[152][152],b[152],jl[152][152],ans,g,sum;
char t;
struct rec
{
	int x,y;
}a[152];
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	  scanf("%d %d",&a[i].x,&a[i].y);
	memset(f,0x7f,sizeof(f));//初值
	memset(jl,0x7f,sizeof(jl));
	g=f[0][0];//記錄最大值
	for (int i=1;i<=n;i++)
	  {
	  	getchar();//換行
	  	for (int j=1;j<=n;j++)
	  	  {
	  	  	t=getchar();//輸入
			jl[i][j]=sqrt(double((a[i].x-a[j].x)*(a[i].x-a[j].x))+double((a[i].y-a[j].y)*(a[i].y-a[j].y)));//用勾股定理求距離
			if (t=='1') f[i][j]=jl[i][j];//f用來求最短路
	  	  }
	  }
	for (int k=1;k<=n;k++)
	  for (int i=1;i<=n;i++)
	    for (int j=1;j<=n;j++)
	      if ((i!=j)&&(j!=k)&&(k!=i)&&(f[i][k]+f[k][j]<f[i][j]))
	        f[i][j]=min(f[i][k]+f[k][j],f[i][j]);//求最短路
	for (int i=1;i<=n;i++)
	  {
	  	for (int j=1;j<=n;j++)
	    if (f[i][j]!=g)
	      b[i]=max(f[i][j],b[i]);//求距離最長的
	    sum=max(sum,b[i]);//記錄
	  }
	ans=g;
	for (int i=1;i<=n;i++)
	  for (int j=1;j<=n;j++)
	    if ((f[i][j]==g)&&(i!=j))//要不同區
	      ans=min(ans,b[i]+b[j]+jl[i][j]);//連接
	printf("%.6lf",max(ans,sum));//輸出最大的
	return 0;
}

溫馨提示:

在luogu中要用scanf("%s")來一行一行地輸入字符數組