欢迎来到endl的博客hhh☀☾☽♡♥

浏览器标题切换
把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

扩散(信息学奥赛一本通 1437)(洛谷 1661)

题目描述

一个点每过一个单位时间就会向四个方向扩散一个距离,如图。

两个点a、b连通,记作e(a,b),当且仅当a、b的扩散区域有公共部分。连通块的定义是块内的任意两个点u、v都必定存在路径e(u,a0),e(a0,a1),…,e(ak,v)。给定平面上的n给点,问最早什么时刻它们形成一个连通块。

输入格式

第一行一个数n,以下n行,每行一个点坐标。

数据规模

对于20%的数据,满足1≤N≤5; 1≤X[i],Y[i]≤50;

对于100%的数据,满足1≤N≤50; 1≤X[i],Y[i]≤10^9。

输出格式

一个数,表示最早的时刻所有点形成连通块。

输入输出样例

输入 #1

2
0 0
5 5

输出 #1

5


事实上,看到题目以后,尽管知道是二分的思路,然鹅我还是不知道该从何下手...(;´ρ`)

后来在网上看到了这位大神的题解,感觉真是豁然开朗,我把两种代码都写了一遍,并分别在ybt和洛谷上提交了,并且全部AC了,感谢大佬提供的思路!

点击即可查看大神思路(真的佩服得五体投地)


floyd+dp

首先我们仔细审一遍题——emm,连通块、任意两点间路径……诶好像有点最短路径的想法!

我们可以这样想,任意两点间的最短距离,由于单位时间内扩散一个单位距离,所以其实也就是两点连通的最短时间的两倍(两点同时扩散)

这里可以用floyd,并不会超时,但是需要注意因为扩散是所有点同时进行的,所以两点之间的距离(d[i][j])推导式要进行一点点变形:

d[i][j]=min(d[i][j],max(d[i][k],d[k][j]));(应该很好理解吧(* ̄︶ ̄))

在进行预处理的时候,任意两点间的距离

但也可以直接

这样在代码中更好运用

最后用两层循环求出图中两点间的 最大的 最短距离 即可,但这还不是答案啦(*๓´╰╯`๓),真正的答案需要再+1、/2,因为这道沙雕题目的设定是——当两点扩散的范围交叉于一点时,是不算“衔接”的,所以还要加上一才行,至于除以二之前已经说过啦

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1e6+5;
 4 int x[N],y[N],fa[N],d[100][100];
 5 int n;
 6 int m,q,ans,p,tot;
 7 int read()
 8 {
 9     int f=1;char ch;
10     while((ch=getchar())<'0'||ch>'9')
11         if(ch=='-')f=-1;
12     int res=ch-'0';
13     while((ch=getchar())>='0'&&ch<='9')
14         res=res*10+ch-'0';
15     return res*f;
16 }
17 void write(int x)
18 {
19     if(x<0)
20     {
21         putchar('-');
22         x=-x;
23     }
24     if(x>9)write(x/10);
25     putchar(x%10+'0');
26 }
27 
28 
29 int main()
30 {
31     n=read();
32     for(int i=1;i<=n;i++)
33         x[i]=read(),y[i]=read();
34     for(int i=1;i<n;i++)
35         for(int j=i+1;j<=n;j++)
36             d[i][j]=d[j][i]=abs(x[i]-x[j])+abs(y[i]-y[j]);
37     for(int k=1;k<=n;k++)
38         for(int i=1;i<=n;i++)
39             if(k!=i)
40                 for(int j=1;j<=n;j++)
41                     if(j!=k&&j!=i)
42                         d[i][j]=min(d[i][j],max(d[i][k],d[k][j]));
43     for(int i=1;i<n;i++)
44         for(int j=i+1;j<=n;j++)
45             ans=max(ans,d[i][j]);
46     write((ans+1)/2);
47     return 0;
48 }
floyd+dp 代码

 

并查集+二分

欸看到上面的“两点间的 最大的 最短距离 ” 是不是感jio很熟悉鸭,这就是肥肠典型的二分答案模型啦

所以这道题肯定可以用二分的方法去做,但是关键就在于该用什么来实现我们二分答案的check咧?

之前提到的那位大神就想出了“并查集”的方法(不得不说,真是太妙了)!

每当我们二分出一个mid值时,都用两个循环先判断两点之间距离是否小于两倍的时间,若是就并在同一个集合里。

注意在check函数中要先将每个点的father赋值为自己本身

判断结束之后就可以再用一次循环遍历每个点,一旦出现有两个点father值都为自己本身(就说明mid不能满足使所有点都在这个时间内连通),就可以直接返回false,将之前二分的 l 变成mid+1,增大时间;

但是如果满足只有一个点的father是其本身的话,那就可以返回true,将二分边界 r 变为mid-1(还得用一个ans变量来存储最后的答案)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1e6+5;
 4 int x[N],y[N],fa[N],d[100][100];
 5 int n;
 6 int m,q,w,ans,p,tot;
 7 int read()
 8 {
 9     int f=1;char ch;
10     while((ch=getchar())<'0'||ch>'9')
11         if(ch=='-')f=-1;
12     int res=ch-'0';
13     while((ch=getchar())>='0'&&ch<='9')
14         res=res*10+ch-'0';
15     return res*f;
16 }
17 void write(int x)
18 {
19     if(x<0)
20     {
21         putchar('-');
22         x=-x;
23     }
24     if(x>9)write(x/10);
25     putchar(x%10+'0');
26 }
27 int find(int x)
28 {
29     if(fa[x]!=x)fa[x]=find(fa[x]);
30     return fa[x];
31 }
32 bool check(int t)
33 {
34     for(int i=1;i<=n;i++)fa[i]=i;
35     for(int i=1;i<n;i++)
36         for(int j=i+1;j<=n;j++)
37             if(d[i][j]<=t*2)
38             {
39                 int fx=find(i),fy=find(j);
40                 if(fx!=fy)fa[fy]=fx;
41             }
42     int sum=0;
43     for(int i=1;i<=n;i++)
44     {
45         if(fa[i]==i)sum++;
46         if(sum==2)return false;
47     }
48     return true;
49 }
50 int main()
51 {
52     n=read();
53     for(int i=1;i<=n;i++)
54         x[i]=read(),y[i]=read();
55     for(int i=1;i<n;i++)
56         for(int j=i+1;j<=n;j++)
57             d[i][j]=d[j][i]=abs(x[i]-x[j])+abs(y[i]-y[j]);
58     int l=0,r=999999999;
59     while(l<=r)
60     {
61         int mid=l+r>>1;
62         if(check(mid))r=mid-1,ans=mid;
63         else l=mid+1;
64     }
65     write(ans);
66     return 0;
67 }
并查集+二分 代码

但其实我个人认为这个代码还可以进一步优化,不知道有没有大佬能帮忙优化一下丫(≧∀≦)ゞ

最后,如果认为我b得还不错的话,不如给个“推荐”吧~


posted @ 2019-08-22 08:34  endl\n  阅读(922)  评论(0编辑  收藏  举报