洛谷P1783 海灘防禦

鏈接:P1783

題目描述
WLP同學最近迷上了一款網絡聯機對戰遊戲(終於知道爲毛JOHNKRAM每天刷洛谷效率那麼低了),但是他卻爲了這個遊戲很苦惱,因爲他在海邊的造船廠和倉庫總是被敵方派人偷襲。於是,WLP動用了他那豐滿且充實的大腦(或許更偏向前者),想出了一個好主意,他把海灘分成垂直於海岸線的若干列,在其中的幾列上放置幾個信號塔,試圖來監視整個海灘。然而,WLP是一個非常心急的人,他把信號塔建好後才發現還需給信號塔供能,它們才能投入使用(這不是廢話麼),它們都有一個工作半徑,一個圓形區域裏的所有敵人都逃不過它們的監視,不過,WLP發現,敵人們非常狡猾,除非他將道路完全封死,否則WLP的敵人可以走過一條任意彎曲的路(不一定走整點,但是不會出第0列和第N列構成的邊界)來偷他的東西。

於是,WLP就思考了:到底需要給每個信號塔多大的工作半徑,才能將從海灘到內地的路徑完全封死呢?他再次動用了他那豐滿且充實的大腦,想了一堂數學課,終於,還是沒想出來。於是,他向LZZ神犇求助(額……C_SUNSHINE的身份是不是暴露了)。

終於,在WLP:“%!*@#!*(*!*#@$^&(此處省略無數賣萌場景)”的哀求下,LZZ神犇寫了一個程序,在1s內就解決了問題。但是,邪惡的LZZ神犇決定要將這個難題共享給無數無辜的OIer,所以,現在輪到你了。

輸入格式
第一行兩個整數N和M:表示海灘被WLP分成的列數0-N和信號塔個數。

第2-M+1行:每行兩個數Xi,Yi表示1-M號信號塔所在的列數和離開海灘的距離。

輸出格式
一行一個實數,表示最小的工作半徑,保留兩位小數。

輸入輸出樣例
輸入 #1
5 5
1 5
3 5
5 5
4 30
2 15
輸出 #1
1.00
輸入 #2
100 2
30 50
90 100
輸出 #2
39.05
說明/提示
對於10%的數據:1≤M≤10,1≤Yi≤100;

對於30%的數據:1≤M≤50,1≤Yi≤1,000;

對於80%的數據:1≤M≤500,1≤Yi≤1,000;

對於100%的數據:1≤M≤800,1≤N≤1000,1≤Xi≤N,1≤Yi≤100,000.

【樣例解釋】

注意,封鎖海灘是指,敵人的深入程度是有限制的,若敵人繞過了所有的信號塔,並且可以長驅直入,那麼就說明道路沒有完全封鎖。


本題大意就是尋求到一個每個圓的最小半徑,使得左右連通,就是差不多下圖的樣子
在這裏插入圖片描述
這道題有點像2017NOIP提高D2T1,不是有點像,幾乎差不多。
主要思路是通過二分答案的方法來找到最小半徑
我們在判斷當前二分到的答案的這個半徑的圓相鄰的是否可以相切或者相交,那麼就用並查集合並這兩個圓(什麼玩意 )最後判斷合併之後的最左邊與最右邊的圓是否也與邊界相切或者相交,如果相切或相交則說明設定這個圓的半徑可以使整個圖連通起來。

代碼:

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
struct CZP
{
	int x,y;
	double dis;
}a[1000001];
int fa[10001],f[10001][2],n,m,len;
double js(int x1,int y1,int x2,int y2)
{
	return sqrt(1.0*(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int cmp(CZP a,CZP b)
{
	return a.dis<b.dis;
}
int find(int x)
{
	if (x==fa[x])
	return x;
	else
	fa[x]=find(fa[x]);
	return fa[x];
}  //並查集路徑壓縮查找操作
void he(int x,int y)
{
	int x1=find(x),y1=find(y);
	if (x1!=y1)
	fa[y1]=x1;
	return ;
}  //並查集合並操作
int check(double k)
{
	for (int i=0;i<n;i++)
	fa[i]=i;  //初始化
	int i=1;
	while (a[i].dis<=2*k && i<=m)
	{
		he(a[i].x,a[i].y);
		i++;   //如果兩點之間距離小於等於2*k則說明這兩個圓相切或相交,則可以合併起來
	}
	for (int i=0;i<n;i++)
	for (int j=0;j<n;j++)
	if (find(i)==find(j) && f[i][0]-k<0 && f[j][0]+k>len)  //若連通最兩邊的點能夠與邊界相連就說明整個圖就肯定連通了
	return 1;
	return 0;
}
int main()
{
	scanf("%d%d",&len,&n);
	for (int i=0;i<n;i++)
		scanf("%d%d",&f[i][0],&f[i][1]);
	for (int i=0;i<n;i++)
	for (int j=i+1;j<n;j++)
	{
		m++;
		a[m].x=i;
		a[m].y=j;
		a[m].dis=js(f[i][0],f[i][1],f[j][0],f[j][1]); //將每兩個點之間的距離存起來
	}
	sort(a+1,a+m+1,cmp);  //按照距離大小排序,以便後面判斷操作
	double L=0.0,R=100000000.0; 
	while (L+0.001<R)  //因爲保留兩位小數,所以精確度在0.001
	{
		double mid=(L+R)/2;
		if (check(mid))
		{
			R=mid;
		}
		else
		L=mid;
	}
	printf("%.2lf",R);
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章