USACO:Cow Tours解题报告

本文介绍了一种计算牧场中任意两点间最远距离的算法。首先使用洪水填充法找到所有连通区域,接着利用弗洛伊德算法计算区域内任意两点间的最短路径,并通过连接不同连通区域来寻找整个牧场的最大直径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思路:先用floodfill得到各连通区域,然后用floyd-warshall得到各连通区域中各点间的最短路径,最后枚举连接不在同一连通区域中的两点并计算新的直径。需要注意的是:通过这样连接得到的直径也许不是整个牧场中最远两个牧区的距离,因为它有可能比期中某个子连通区域的直径小,即最远距离的两个牧区可能就在连接之前的某个连通区域中,因此在最后需要和每个连通区域中的最远距离比较从而得到最终结果。

 

 

/*
ID: xpli1
PROG: cowtour
LANG: C
*/

#include <stdio.h>
#include <math.h>

#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))

#define IN stdin
#define OUT stdout

#define MAX_SIZE  160

unsigned short mx[MAX_SIZE][MAX_SIZE];
double dis[MAX_SIZE][MAX_SIZE];
double max_dis[MAX_SIZE];
int cx[MAX_SIZE],cy[MAX_SIZE];
int comp[MAX_SIZE];
char buf[MAX_SIZE];
double MIN;
int N;


void flood_fill(int new_comp)
{
 int num_visited,i,j;
 do{
  num_visited = 0;

  for(i=0; i<N; i++){
   if(comp[i] == -1){
    num_visited++;
    comp[i] = new_comp;
    for(j=0; j<N; j++){
     if(mx[i][j]){
      if(comp[j] == -2)
       comp[j] = -1;
     }
    }
   }
  }
 } while (num_visited);

 return;
}

void find_comp()
{
 int new_comp = 0,i;

 for(i=0; i<N; i++)
  comp[i] = -2;      // 未扩展且未访问

 for(i=0; i<N; i++){
  if(comp[i] == -2){
   new_comp++;
   comp[i] = -1;  //  扩展但未访问
   flood_fill(new_comp);
  }
 }
 return;
}

void floyd_warshall()
{
 int i,j,k;
 
 for(i=0; i<N; i++){
  for(j=0; j<N; j++){
   if(mx[i][j] || i==j) dis[i][j] = sqrt((cx[i]-cx[j])*(cx[i]-cx[j])+(cy[i]-cy[j])*(cy[i]-cy[j]));
   else dis[i][j] = 1000000;
  }
 }

 for(k=0; k<N; k++){
  for(i=0; i<N; i++){
   for(j=0; j<N; j++){
    if(comp[i] == comp[j] && comp[j] == comp[k]){
     if(dis[i][k] + dis[k][j] < dis[i][j])
      dis[i][j] = dis[i][k] + dis[k][j];
    }
   }
  }
 }

 for(i=0; i<N; i++){
  max_dis[i] = -1;
  for(j=0; j<N; j++){
   if(comp[i] == comp[j]){
    if(max_dis[i] < dis[i][j]) max_dis[i] = dis[i][j];
   }
  }
 }

 return;
}

void connect_check()
{
 int i,j;

 MIN = 1000000;

 for(i=0; i<N; i++){
  for(j=0; j<N; j++){
   if(comp[i] != comp[j]){
    if(MIN > max_dis[i] + max_dis[j] + sqrt((cx[i]-cx[j])*(cx[i]-cx[j])+(cy[i]-cy[j])*(cy[i]-cy[j])))
     MIN = max_dis[i] + max_dis[j] + sqrt((cx[i]-cx[j])*(cx[i]-cx[j])+(cy[i]-cy[j])*(cy[i]-cy[j]));
   }
  }
 }

 for(i=0; i<N; i++){
  if(MIN < max_dis[i])
   MIN = max_dis[i];
 }
 return;
}

int main(void)
{
 FILE* fin = fopen("cowtour.in","r");
 FILE* fout = fopen("cowtour.out","w");

 fscanf(IN,"%d",&N);

 int i,j;

 for(i=0; i<N; i++) 
  fscanf(IN,"%d%d/n",&cx[i],&cy[i]);

 for(i=0; i<N; i++){
  fgets(buf,sizeof(buf),IN);
  for(j=0;j<N; j++){
   mx[i][j] = buf[j] - '0';
  }
 }

 find_comp();

 floyd_warshall();

 connect_check();
 
 fprintf(OUT,"%.6lf/n",MIN);
 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值