P2498 [SDOI2012]拯救小云公主(並查集/Prim)
阿新 • • 發佈:2022-03-30
題意
給定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 等價於左下邊走障礙點可以八連通走到右上邊
(四和八交換也是成立的)