#Star Way To Heaven 优化二分 /prim<凉宫春日的忧郁>

之前是贪心看不粗来,现在二分也看不粗来了,QAQ。。。

审题清楚,别没看懂题就忙着暴搜,小w不一定走整点,可以走直线

其实,看到就应该想到二分了

那么其实二分的答案是作为每个点的半径以圆的形式出现的,一个答案是否成立,看这些障碍圆建出来后,是否还能从左到右联通,那其实是看障碍组成的边界线是否能联通上下的边界。并查集,dfs都可维护。然后是T80的好成绩。

考虑优化,每次并查集会枚举所有点判距离(毕竟6000^2开不下),这其中是有冗余状态的,考虑这种情况,a可以和他最近的b相连,b可以和他最近的c相连,a也可以和c相连,这是三点其实只用枚举两次就可以,然而枚举c时就多枚举了。优化就显而易见的只枚举四个方向的最近点。

 1 #include<bits/stdc++.h>
 2 #define MAXN 6100
 3 #define reg register 
 4 #define INF 1000000000.0
 5 using namespace std;
 6 inline int read(){
 7     int s=0,w=0;char ch=getchar();
 8     while(ch<'0'||ch>'9')w=(ch=='-'),ch=getchar();
 9     while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
10     return w?-s:s;
11 }
12 #define kd (read())
13 int N,M,K;
14 int fat[MAXN];
15 struct dian{
16     int x,y;
17 }yd[MAXN];//圆点
18 int sf[MAXN][4];
19 inline int find(int x){
20     if(!fat[x]||fat[x]==x)return x;
21     return fat[x]=find(fat[x]);
22 }
23 void del(){for(reg int i=1;i<=K+2;++i)fat[i]=0; }//清并查集
24 double l,r;
25 double cal(int a,int b){
26     if(a==0||b==0)return INF;
27     return 1.0*sqrt(1.0*(yd[a].x-yd[b].x)*(yd[a].x-yd[b].x)+
28     1.0*(yd[a].y-yd[b].y)*(yd[a].y-yd[b].y)); 
29 }
30 bool _judge(double dist){
31     del();
32     /*for(reg int i=1;i<=K;++i)
33         for(reg int j=1;j<i;++j)
34             if(cal(yd[i],yd[j])<2.0*dist)
35                 fat[find(i)]=find(j);*/
36     for(reg int i=1;i<=K;++i)
37         for(reg int k=0;k<4;++k)
38             if(sf[i][k])
39                 if(find(i)!=find(sf[i][k]))
40                     if(cal(i,sf[i][k])<2.0*dist)
41                         fat[find(i)]=find(sf[i][k]);
42     for(reg int i=1;i<=K;++i){
43         if(1.0*M-yd[i].y<2.0*dist)
44             fat[find(K+1)]=find(i);
45         if(yd[i].y<2.0*dist)
46             fat[find(K+2)]=find(i);
47         if(find(K+2)==find(K+1))
48             return false;
49     }
50     if(find(K+2)==find(K+1))
51         return false;
52     return true;
53 }
54 int main(){
55     //freopen("da.in","r",stdin);
56     N=kd;M=kd;K=kd;
57     for(reg int i=1,a,b;i<=K;++i)
58         yd[i].x=kd,yd[i].y=kd;
59     for(reg int i=1;i<=K;++i){
60         for(reg int j=1;j<=K;++j)
61             if(i!=j){
62                 double dis=cal(i,j);
63                 if(yd[j].x<=yd[i].x&&yd[j].y<=yd[i].y)
64                     if(dis<cal(i,sf[i][0]))
65                         sf[i][0]=j;
66                 if(yd[j].x>=yd[i].x&&yd[j].y>=yd[i].y)
67                     if(dis<cal(i,sf[i][1]))
68                         sf[i][1]=j;
69                 if(yd[j].x<=yd[i].x&&yd[j].y>=yd[i].y)
70                     if(dis<cal(i,sf[i][2]))
71                         sf[i][2]=j;
72                 if(yd[j].x>=yd[i].x&&yd[j].y<=yd[i].y)
73                     if(dis<cal(i,sf[i][3]))
74                         sf[i][3]=j;
75             }
76         //for(int k=0;k<4;++k)
77         //    cout<<sf[i][k]<<" ";
78         //cout<<endl;
79     }
80     l=0;r=1000000.0;
81     while(r-l>(1e-8)){
82         double mid=(l+r)*0.5;
83         if(_judge(mid))l=mid;
84         else r=mid;
85     }
86     printf("%.7lf\n",r);
87 }
View Code

跑prim,O(n^2),贼快

其实思想差不多,每个点直接按距离连边,跑出来后找两个边界的路径的最大值除二,表示我找到一个权值最小的边界,在这个边界上最大只能是最大边除二。进一步解释,我过这条障碍线最大要为大边除二。在权值最小的防线上可以这样通过,那么在其他防线上(权值一定比此权值大)也一定能成立

转载于:https://www.cnblogs.com/2018hzoicyf/p/11370514.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
这段代码的时间复杂度是 $O(n^2)$,其中 $n$ 是顶点的数量。我们可以通过使用堆来优化 Prim 算法的时间复杂度,使其变为 $O(m \log n)$,其中 $m$ 是边的数量。具体来说,我们可以将距离存储在堆中,并在每次更新距离时更新堆。这样,每次从堆中弹出的元素都是当前未访问顶点中距离最小的。 修改后的代码如下: ```c++ #include <iostream> #include <vector> #include <queue> #include <limits> using namespace std; const float INF = numeric_limits<float>::infinity(); int main() { while (true) { try { int n, e; cin >> n >> e; vector<vector<float>> edges(n, vector<float>(n, INF)); for (int i = 0; i < e; ++i) { int a, b, c; cin >> a >> b >> c; edges[a][b] = edges[b][a] = c; } // Prim algorithm vector<float> dist(n, INF); dist[0] = 0; vector<bool> visited(n, false); priority_queue<pair<float, int>, vector<pair<float, int>>, greater<pair<float, int>>> heap; heap.emplace(0, 0); while (!heap.empty()) { int u = heap.top().second; heap.pop(); visited[u] = true; for (int v = 0; v < n; ++v) { if (!visited[v] && edges[u][v] < dist[v]) { dist[v] = edges[u][v]; heap.emplace(dist[v], v); } } } int ans = 0; float min_sum = INF; for (int i = 0; i < n; ++i) { float sum_dist = 0; for (int j = 0; j < n; ++j) { sum_dist += edges[i][j]; } if (sum_dist < min_sum) { min_sum = sum_dist; ans = i; } } cout << ans << endl; } catch (const exception& e) { break; } } return 0; } ``` 修改后的代码使用了一个最小堆来存储距离,堆的元素是一对值 $(d, u)$,其中 $d$ 是 $u$ 到当前生成树的距离,$u$ 是顶点编号。每次从堆中弹出的元素就是当前未访问顶点中距离最小的。在更新距离时,我们只需要将新的距离和对应的顶点加入堆中即可。这样,每个顶点最多会被访问一次,每次访问的时间复杂度为 $O(\log n)$。因此,总时间复杂度为 $O(m \log n)$。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值