Usaco 2.4 Cow Tours(Floyd)

来源:互联网 发布:ubuntu界面怎么启动u盘 编辑:程序博客网 时间:2024/05/29 15:15
Cow Tours

Farmer John has a number of pastures on his farm. Cow paths connect some pastures with certain other pastures, forming a field. But, at the present time, you can find at least two pastures that cannot be connected by any sequence of cow paths, thus partitioning Farmer John's farm into multiple fields.

Farmer John would like add a single a cow path between one pair of pastures using the constraints below.

A field's `diameter' is defined to be the largest distance of all the shortest walks between any pair of pastures in the field. Consider the field below with five pastures, located at the points shown, and cow paths marked by lines:                 15,15   20,15                  D       E                  *-------*                  |     _/|                  |   _/  |                  | _/    |                  |/      |         *--------*-------*         A        B       C         10,10   15,10   20,10

The `diameter' of this field is approximately 12.07106, since the longest of the set of shortest paths between pairs of pastures is the path from A to E (which includes the point set {A,B,E}). No other pair of pastures in this field is farther apart when connected by an optimal sequence of cow paths.

Suppose another field on the same plane is connected by cow paths as follows:                          *F 30,15                         /                        _/                       _/                        /                         *------                    G      H                   25,10   30,10

In the scenario of just two fields on his farm, Farmer John would add a cow path between a point in each of these two fields (namely point sets {A,B,C,D,E} and {F,G,H}) so that the joined set of pastures {A,B,C,D,E,F,G,H} has the smallest possible diameter.

Note that cow paths do not connect just because they cross each other; they only connect at listed points.

The input contains the pastures, their locations, and a symmetric "adjacency" matrix that tells whether pastures are connected by cow paths. Pastures are not considered to be connected to themselves. Here's one annotated adjacency list for the pasture {A,B,C,D,E,F,G,H} as shown above:                 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

Other equivalent adjacency lists might permute the rows and columns by using some order other than alphabetical to show the point connections. The input data contains no names for the points.

The input will contain at least two pastures that are not connected by any sequence of cow paths.

Find a way to connect exactly two pastures in the input with a cow path so that the new combined field has the smallest possible diameter of any possible pair of connected pastures. Output that smallest possible diameter.

PROGRAM NAME: cowtour

INPUT FORMAT

Line 1:An integer, N (1 <= N <= 150), the number of pasturesLine 2-N+1:Two integers, X and Y (0 <= X ,Y<= 100000), that denote that X,Y grid location of the pastures; all input pastures are unique.Line N+2-2*N+1:lines, each containing N digits (0 or 1) that represent the adjacency matrix as described above, where the rows' and columns' indices are in order of the points just listed.

SAMPLE INPUT (file cowtour.in)

810 1015 1020 1015 1520 1530 1525 1030 100100000010111000010010000100100001110000000000100000010100000010

OUTPUT FORMAT

The output consists of a single line with the diameter of the newly joined pastures. Print the answer to exactly six decimal places. Do not perform any special rounding on your output.

SAMPLE OUTPUT (file cowtour.out)

22.071068

分析转自NOCOW:

预先生成所有在接下来的计算中会多次用到的数据(比如任意两点间距离);这是优化;
给菜鸟:从最朴素到高效优化

1、你的最朴素想法是什么?

显然是利用Floyd生成原始情况,然后每条没有连接的边枚举过去,重新Floyd计算一下最大直径。

经测试,这种想法在第4个测试数据上用时近半秒,然后第5个点你等了2分钟还没有出解。

这样做,你的时间复杂度保证有O(N^5)吧?

150这个点数不容小觑啊。

2、让我们开始慢慢优化

优化1、

既然不是不同片区的两点,加上连线没有用(还出错解),那就不用加了。

用什么知道每个点属于哪个片区?方法五花八门,我用了并查集。

优化2、

预先生成所有在接下来的计算中会多次用到的数据(比如任意两点间距离)

优化3、

没意义的计算不要做了!

每条枚举的边保证编号前小后大;

for i:=1 to n do       for j:=i+1 to n do

如果接上这条边,预期直径比之前算到的最小直径都大就抛弃;

if dis[i,0]+pointdis[i,j]+dis[j,0]>minnum then continue//说明:dis[i,0]表示在i所在的区块内到i的最远距离【不是该区块的直径】,pointdis[i,j]表示i、j两点间直接连接的距离,minnum表示当前得到的最小直径

else floyd;

这样下来,第4个点可以秒杀了,但是第5个点(只有两个区块,随机性弱)还需要5秒+,第7个点(多区块,随机性强)需要20秒+

3、绕开错误,走向更高效

错误优化:

dis[i,0]+pointdis[i,j]+dis[j,0]>minnum

既然这个是判断条件而且也正确,那么满足这个判断条件的就可以通过dis[i,0]+pointdis[i,j]+dis[j,0]得到最短直径。

if dis[i,0]+pointdis[i,j]+dis[j,0]>minnum then continue else minnum:=dis[i,0]+pointdis[i,j]+dis[j,0];

然后很高兴,第5个超时的点秒杀,但是第7个点立刻给你摆了一道。

原因:在多区块的时候,首先,你连通的两区块的直径不一定满足dis[i,0]+pointdis[i,j]+dis[j,0];

4     |     | 2   2 3     |     | 2     5

(2,3直连距离为1;3,4与4,5两两直连距离为2,此时最大直径为4)

4   |   | 2-3   |   |   5

(连接2,3,然后根据式子你会错误得到直径为3,其实仍为4)

其次,你联通两区块得到的新区块的直径很可能比其余区块的直径小

4    |    |1 2 3    |    |    5

(1,2与2,3直连距离为1;3,4与4,5两两直连距离为2,此时最大直径为4)

4    |    |1-2 3    |    |    5

(连接1,2,然后根据式子你会错误得到直径为2,其实仍为4)

正确的优化:

其实仔细想想,我们关注的只是区块的直径,那我们只需要计算新连成的区块的直径,然后与原来的所有区块的直径放在一起,取最大直径就可以了。

tempmax:=dis[i,0]+pointdis[i,j]+dis[j,0];//计算新连成的区块的直径               for k:=1 to n do                   if blockdis[k]>tempmax then tempmax:=blockdis[k];//检查别的区块的直径是否比新连成的区块的直径大               minnum:=tempmax;
/*ID:shi13891LANG:C++TASK:cowtour*/#include <iostream>#include <cstdio>#include <cstring>#include <math.h>using namespace std; #define INF 0xfffffff int n,x[165],y[165];double map[165][165],res=INF,Max[165]; double dist(int i,int j){       return(sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])));} void Init(){    for (int i=1;i<=n;i++)    {        for (int j=1;j<=n;j++)        {            char c;            cin>>c;            if(c=='1') map[i][j]=dist(i,j);            else                map[i][j]=INF;            if (i==j)                map[i][j]=0;        }    }} 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 (map[i][k]+map[k][j]<map[i][j])                   map[i][j]=map[i][k]+map[k][j];            }        }    }} void Solve(){    for (int i=1;i<=n;i++)    {        for (int j=1;j<=n;j++)        {            if ((map[i][j]!=INF)&&(map[i][j]>Max[i]))               Max[i]=map[i][j];        }    }    for (int i=1;i<=n;i++)    {        for (int j=1;j<=n;j++)        {            if (map[i][j]==INF)            {                if (dist(i,j)+Max[i]+Max[j]<res)                {                    res=dist(i,j)+Max[i]+Max[j];                }            }        }    }    for (int i=1;i<=n;i++)        if (Max[i]>res)        res=Max[i];    printf("%.6lf\n",res);} int main(){    freopen("cowtour.in", "r", stdin);    freopen("cowtour.out", "w", stdout);    cin>>n;    for (int i=1;i<=n;i++)        cin>>x[i]>>y[i];    memset(Max,0,sizeof(Max));    Init();    Floyd();    Solve();    return 0;}

原创粉丝点击