洛谷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号信号塔所在的列数和离开海滩的距离。

输出格式:

一行一个实数,表示最小的工作半径,保留两位小数。

说明:

对于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.


题目分析:

这里我们可以借鉴一下最小生成树的思想
将每个防御塔看作节点,并计算其两两间的距离作为边
此时图中只有节点而无边

将所有边按长度从小到大排序
然后依次加入这些边
每加入一条边就dfs遍历整个图
若此时图中某个联通块可以遍历到左右边界
此时的边权即为所求答案

还有几点需要注意的是

  • 由于所求的是最小半径,所以计算边权的时候要将两点间距离除以二作为边权
  • 海滩的列数编号为0~n,而0与n不能通行,所以左右边界为1与n-1

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;

const int maxn=1000010;
typedef double dd;
int n,m;

struct node{dd x,y;}pt[maxn];
struct edge{int u,v;dd dis;}E[1000010];

bool vis[maxn];
bool lft,rht;//判断是否联通左右边界 
vector<int> map[maxn]; 


dd dist(node a,node b)
{
	dd tx=fabs(a.x-b.x);
	dd ty=fabs(a.y-b.y);
	tx*=tx;
	ty*=ty;
	return sqrt(tx+ty);
}

void dfs(int u,dd dis)
{
	vis[u]=true;
	
	//若到达左/右边界则记录 
	if(pt[u].y+dis>=n-1) 
	rht=true;
	
	if(pt[u].y-dis<=1)
	lft=true;
	
	for(int j=0;j<map[u].size();j++)
	{
		int v=map[u][j];
		
		if(vis[v]==false)
		dfs(v,dis);
	}
}

bool cmp(edge a,edge b)
{
	return a.dis<b.dis;
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>pt[i].y>>pt[i].x;
		for(int j=1;j<i;j++)//在这里建边 
		{
			E[++cont].u=i;
			E[cont].v=j;
			E[cont].dis=dist(pt[i],pt[j])/2.0;
			//记得边权=两点距离/2.0 
		}
	}
	
	sort(E+1,E+1+cont,cmp);
	 
	for(int i=1;i<=cont;i++)//从小到大加入每条边 
	{
		map[E[i].u].push_back(E[i].v);
        map[E[i].v].push_back(E[i].u);
        
        for(int j=1;j<=m;j++)
        {
        	if(vis[j]==false)
        	dfs(j,E[i].dis);
        		
        	if(lft&&rht)//若联通则直接输出并退出程序 
        	{
        		printf("%.2lf",E[i].dis);
        		return 0;
        	}
        	
        	else
        	lft=rht=false;
        	//注意判断封锁要在同一联通快内,所以每次都要重置 
        }
        memset(vis,false,sizeof(vis));
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值