P2498 [SDOI2012]拯救小云公主(并查集/Prim)
作者:互联网
题意
给定n个boss的坐标,英雄在左下角(1,1),公主在右上角(row,line),英雄决定找一条路径使到距离boss的最短距离最远。
Ps:英雄走的方向是任意的,但是不能走出矩形的范围。即英雄可以到达矩形范围内的任意一个点(没有必要是整点)
输入格式
n表示boss的数目,row,line表示矩形的大小;
接下来n行,每行分别两个整数表示boss的位置坐标。
输出格式
输出一个小数,表示英雄的路径离boss的最远距离,精确到小数点后两位
样例
input
1 3 3
2 2
output
1.00
思路
假设我们现在知道了答案为x,那么我们显然可以将n个boss所在的点转化为n个半径为x的圆(不包括圆的边),那么,也就是说我们可以有一条路径从(1,1)走到(row,line)。所以,第一想法就是二分答案,然后得到所有圆,然后判断能否从起点走到终点。
此时的问题就在于我们该如何判断是否有解呢。
这里有个很巧妙的思路就是,因为这个图并不是只走整点,所以很难直接bfs几个方向然后判断是否存在一条路径到达,于是,这里的转化就出现了,我们判断从左上角是否可以通过这些圆(并查集维护)抵达右下角,也就是说,只走障碍,看看能不能障碍直接将路隔开。
以上的方法是 O(n^2*log())
如果模拟过画圆的感觉,其实我们可以有个比较巧妙的感觉,就是这个题的图和最小生成树有点像,就是从左上角和右下角联通时的最小生成树。并且这个图是个稠密图。于是,我们可以考虑跑一个裸的Prim算法,时间复杂度为O(n^2)
code 该代码采用第二个方法
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
//#pragma GCC optimize(3)
#define pb push_back
#define is insert
#define PII pair<int,int>
#define show(x) cerr<<#x<<" : "<<x<<endl;
//mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
//ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}
const int INF=0x3f3f3f3f;//2147483647;
const int N=3050,M=1e5+50;
const ll mod=998244353;
int n,row,line;
struct node {
double x,y;
}a[N];
double cal(int i,int j){
return 0.5*sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));
}
double dis[N];
int vis[N];
double ans=0;
double g[N][N];
void prim(){
for(int i=1;i<=n+2;i++){
dis[i]=10000000000;
}
int s=n+1;
for(int i=1;i<=n+2;i++){
vis[s]=1;
for(int j=1;j<=n+2;j++) {
dis[j] = min(dis[j], g[j][s]);
}
double minn=10000000000,pos=-1;
for(int j=1;j<=n+2;j++){
if(vis[j])continue;
if(minn>dis[j]){
minn=dis[j];
pos=j;
}
}
if(minn!=10000000000){
ans=max(minn,ans);
s=pos;
if(s==n+2){
break;
}
}
}
}
void solve() {
cin>>n>>row>>line;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
}
for(int i=1;i<=n+2;i++){
for(int j=1;j<=n+2;j++){
g[i][j]=10000000000;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
g[i][j]=cal(i,j);
}
}
for(int i=n+1;i<=n+2;i++){
for(int j=1;j<=n;j++){
if(i==n+1)g[i][j]=g[j][i]=min(a[j].x-1,line-a[j].y);
if(i==n+2)g[i][j]=g[j][i]=min(row-a[j].x,a[j].y-1);
}
}
prim();
cout<<fixed<<setprecision(2)<<ans;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int __=1;//cin>>__;
while(__--){
solve();
}
return 0;
}
总结
如果是网格图,左上到右下四连通(只能走上下左右)等价于左下边和右上边八连通(除了上下左右还可以走四种斜对角)
例如(S为起点,T为终点,!为障碍)
S0!
0!0
!0T
那么, S 不能走到 T 等价于左下边走障碍点可以八连通走到右上边
(四和八交换也是成立的)
标签:Prim,int,double,查集,long,boss,line,小云,row 来源: https://www.cnblogs.com/illume/p/16078783.html