USACO/cowtour

【题解】这道题,我想写一下我的整个思路:

首先,我题目没有了解清楚(痛),觉得可能是最小生成树。后来仔细看题目发现是求一段连通图中的最远的两个点的距离,可以用Floyd 求新的图的距离,但怎么考虑连接两个牧场呢,要保证时间效率过得去。然后我突然想到可以向处理出每一个牧场中的相互距离,最后连接就是把各个牧场到连接点的距离求出+那两个点连接的距离,即边(v1,v2),v1 牧场到v1的距离+v2牧场到v2的距离+dis(v1,v2);去一个最小值就是答案。然后我交上去,发现在第7个点的时候WA了,由于数据太大,实在没法调,我看了一下题解(没有自己仔仔细细地思考算法是否存在问题,是个严重的失误),突然醒悟,就是没有考虑到单个牧场中的直径。最后就A了。



/*
ID:CYMXYYM1
TASK:cowtour
LANG:C++
*/

#include<cstdio>
#include<cstring>
#include<cmath>

#define DB double
#define inf 1047483641.0

int tot=0,w[500],xz[500],yz[500],next[500];
int f[500][500],s[500],twice[500],n;
DB d[500][500],from[500];

void dfs(int x)
{
	twice[x]=1;w[x]=tot;
	for(int i=1;i<=n;i++)
	 if((!twice[i]) && (f[x][i]))
	  {
			next[i]=s[tot];s[tot]=i;dfs(i);
	  }
}
inline void dis(int x)
{
	for(int i=s[x];i;i=next[i])
	 for(int j=s[x];j;j=next[j])
	  for(int k=s[x];k;k=next[k])
	   	if(d[j][k]>d[j][i]+d[i][k])d[j][k]=d[j][i]+d[i][k];
}
//平方; 
inline DB fang(DB p){return p*p;}

//求直接距离;
 
inline DB getdis(int xx,int yy){
	return sqrt(fang((DB)xz[yy]-xz[xx])+fang((DB)yz[yy]-yz[xx]));
}
inline DB max(DB x,DB y){return x>y?x:y;}
inline DB min(DB x,DB y){return x<y?x:y;}

int main()
{
	freopen("cowtour.in","r",stdin);
	freopen("cowtour.out","w",stdout);

	char ch;scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d%d",&xz[i],&yz[i]);
	for(int i=1;i<=n;i++)
	{
		while(ch=getchar(),ch=='\n' || ch=='\r');
	 	for(int j=1;j<=n;j++)f[i][j]=(int)ch-48,ch=getchar();
	}
	
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=n;j++)
	  if(i==j)d[i][j]=0;else d[i][j]=inf;
	
	memset(twice,0,sizeof(twice));
	memset(s,0,sizeof(s));
	memset(from,0,sizeof(from));
	
	for(int i=1;i<=n;i++)
	 if(!twice[i])
	 {
			tot++;s[tot]=i;next[i]=0;dfs(i);
	 }
		
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=n;j++)
	  if(f[i][j])d[i][j]=getdis(i,j);
	for(int i=1;i<=tot;i++)dis(i);
	
	for(int i=1;i<=tot;i++)
	 for(int j=s[i];j;j=next[j])
	  for(int k=s[i];k;k=next[k])
	   if(d[j][k]>from[i])from[i]=d[j][k];
	
	DB minx=inf;
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=n;j++) 
	  if(w[i]!=w[j]){
			DB max1=0,max2=0;
			for(int k=s[w[i]];k;k=next[k])
			 if(k!=i)max1=max(max1,d[k][i]);
			for(int k=s[w[j]];k;k=next[k])
			 if(k!=j)max2=max(max2,d[k][j]);
			 
			minx=min(minx,max(max(from[w[i]],from[w[j]]),max1+max2+getdis(i,j)));
	}
	printf("%.6lf\n",minx);
	return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值