USACO cowtour Floyd + 枚举

37 篇文章 0 订阅

给出来的数据量还是可以的。题意:有若干个牧场,至少有两个不连通,一个牧场的直径就是牧场中最远的两个牧区的距离。要求找出几个牧场中最短的直径,就是找一条路径连接几个牧区,使这个直径最终最小。

基本方法,把整个图根据输入划分成几个不连通的牧区,然后求出每个牧区的直径(即每个连通块中的最长路径),然后枚举两个不在同一牧区的点,设blocks[i]记录第i个节点所在连通块的直径,那么result = min(blocks[i] + dis(i, j) + blocks[j]),可以用并查级判断两个点是否连通


/*
ID:kevin_s1
PROG:cowtour
LANG:C++
*/

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <cstdlib>
#include <list>
#include <cmath>

using namespace std;

#define MAXN 175
const double INF = 1E15;

//gobal variable====
int N;

struct point{
	double x;
	double y;
}points[MAXN];

double Graph[MAXN][MAXN];

double result;

int Father[MAXN];
int Rank[MAXN];

double blocks[MAXN];
//==================


//function==========

int Find(int x){
	while(x != Father[x]){
		x = Father[x];
	}
	return x;
}

bool check(int x, int y){
	x = Find(x);
	y = Find(y);
	if(x == y)
		return true;
	else
		return false;
}

void Union(int x, int y){
	x = Find(x);
	y = Find(y);
	if(x == y)
		return;
	if(Rank[x] >= Rank[y]){
		Father[y] = x;
		Rank[x] += Rank[y];
	}
	else{
		Father[x] = y;
		Rank[y] += Rank[x];
	}
}

double dist(point start, point end){
	return sqrt((end.x - start.x)*(end.x - start.x) + (end.y - start.y)*(end.y - start.y));
}

void Input(){
	cin>>N;
	for(int i = 1; i <= N; i++){
		cin>>points[i].x>>points[i].y;
		blocks[i] = -1;
		Father[i] = i;
		Rank[i] = 1;
	}
	char flg;
	for(int i = 1; i <= N; i++)
		for(int j = 1; j <= N; j++){
			cin>>flg;
			if(i == j)
				Graph[i][j] = 0;
			else if(flg == '1'){
				Union(i, j);
				Graph[i][j] = dist(points[i], points[j]);
			}
			else if(flg == '0')
				Graph[i][j] = INF;
		}
}

void Floyd(){
	for(int k = 1; k <= N; k++){
		for(int i = 1; i <= N; i++){
			for(int j = 1; j <= N; j++){
				if(k == i || k == j)
					continue;
				if(Graph[i][j] > Graph[i][k] + Graph[k][j]){
					Graph[i][j] = Graph[i][k] + Graph[k][j];
				}
			}
		}
	}
}

void print(){
	for(int i = 1; i <= N; i++){
		for(int j = 1; j <= N; j++){
			cout<<Graph[i][j]<<" ";
		}
		cout<<endl;
	}
}


//==================

int main(){
	freopen("cowtour.in","r",stdin);
	freopen("cowtour.out","w",stdout);
	Input();
	Floyd();
	for(int i = 1; i <= N; i++){
		for(int j = 1; j <= N; j++){
			if(Graph[i][j] != INF && blocks[i] < Graph[j][i])
				blocks[i] = Graph[j][i];
		}
	}
	
	result = INF;
	for(int i = 1; i <= N; i++){
		for(int j = i + 1; j <= N; j++){
			if(Graph[i][j] == INF && !check(i, j)){
				double dis = dist(points[i], points[j]);
				if(blocks[i] + dis + blocks[j] < result)
					result = blocks[i] + dis + blocks[j];
			}
		}
	}

	for(int i = 1; i <= N; i++)
		if(blocks[i] > result)
			result = blocks[i];

	printf("%.6lf\n", result);
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值