其他分享
首页 > 其他分享> > P2498 [SDOI2012]拯救小云公主(并查集/Prim)

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