• 【USACO2.4.3】【洛谷P1522】牛的旅行【最短路】【并查集】


    题目大意:

    题目链接:

    USACO:http://train.usaco.org/usacoprob2?a=TyEfGmq7aAo&S=cowtour
    洛谷:https://www.luogu.org/problemnew/show/P1522

    有一个无向图,可以在两个不同的联通块中选择其中两个结点并连接,求此时的新联通块的最远两点之间的距离的最小值。


    思路:

    n150nleq150的数据很明显是在提示我们用FloydFloyd做。那么我们就可以求出任意两点之间的距离。
    此时可以选择枚举任意两点i,ji,j,如果这两点不在一个联通块(dis[i][j]>Infdis[i][j]>Inf),那么就可以O(n)O(n)求出这两个点和同一联通块的最远点之间的距离,然后更新答案
    ans=min(ans,sum1+sum2+cal(i,j))ans=min(ans,sum1+sum2+cal(i,j))
    最终取个66位小数就可以了。
    然后就可以得到9090分的高分。
    此时再打个表就过了


    关于#77

    它死了。
    为什么会这样呢?
    我们发现,每次更新ansans的时候,是把新联通块的新加入的边看成最长路径中的一条了。那么有没有可能加入这条边之后最长路依然只存在其中的一个旧联通块中呢?
    有可能。
    那么就得先把所有联通块的最长路求出来,然后再更新ansans的时候再加入两个条件:
    ans=min(ans,max(sum1+sum2+cal(i,j),max(s1,s2)))ans=min(ans,max(sum1+sum2+cal(i,j),max(s1,s2)))
    其中s1,s2s1,s2分别表示新边所连接的两个旧联通块的最长路。
    但是这样就必须用并查集了。
    时间复杂度:O(n3α(n))O(n^3alpha(n))


    代码:

    /*
    ID:ssl_zyc2
    TASK:cowtour
    LANG:C++
    */
    #include <cstdio>
    #include <cmath>
    #include <iostream>
    #include <cstring>
    using namespace std;
    
    const int N=200;
    const double Inf=1e9;
    int n,x[N],y[N],map[N][N],father[N];
    double dis[N][N],sum1,sum2,ans,maxn[N];
    
    double cal(double x1,double x2,double y1,double y2)
    {
    	return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    }
    
    int find(int x)
    {
    	return x==father[x]?x:father[x]=find(father[x]);
    }
    
    int main()
    {
    	freopen("cowtour.in","r",stdin);
    	freopen("cowtour.out","w",stdout);
    	ans=Inf;
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d%d",&x[i],&y[i]);
    		father[i]=i;
    	}
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=n;j++)
    		{
    			scanf("%1d",&map[i][j]);
    			if (map[i][j]==1)
    			{
    				dis[i][j]=cal(x[i],x[j],y[i],y[j]);  //求路径长度
    				father[find(j)]=find(i);
    			}
    			else dis[i][j]=Inf+1.0;
    		}
    	for (int k=1;k<=n;k++)  //Floyd
    		for (int i=1;i<=n;i++)
    			for (int j=1;j<=n;j++)
    				if (i!=j&&j!=k&&k!=i)
    					dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=n;j++)
    			if (find(i)==find(j)&&i!=j)
    				maxn[find(i)]=max(maxn[find(i)],dis[i][j]);  
    				//maxn[i]表示含i的联通块的最长路
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=n;j++)  //枚举任意两点
    			if (i!=j&&find(i)!=find(j))
    			{
    				sum1=0;
    				sum2=0;
    				for (int k=1;k<=n;k++)
    				{
    					if (find(i)==find(k)&&i!=k)
    						sum1=max(sum1,dis[i][k]);
    					if (find(j)==find(k)&&j!=k)
    						sum2=max(sum2,dis[j][k]);
    				}
    				ans=min(ans,max(sum1+sum2+cal(x[i],x[j],y[i],y[j]),max(maxn[find(i)],maxn[find(j)])));
    			}
    	printf("%6lf
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    ORM框架
    优酷项目1
    新年第一天
    前端第十天
    前端第九天
    前端第八天
    前端第七天
    前端第六天
    前端第五天
    月亮与六便士
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998441.html
Copyright © 2020-2023  润新知