题解 P1158 【导弹拦截】
这一道题是智障能推荐给我的,(当时以为是那道DP,因为也是NOIP的),10分钟过去之后,发现自己只能想出
预备知识:贪心,后缀数组(后缀最大值)。
首先有一个很明显的道理:当两个系统的最远距离都和某个(可能是不同的点)重合时,答案比不重合更优。
如下图,上面的显然比下面的更优
接下来就是考虑系统的边缘落在哪个点上的问题,观察用脑子想想容易得到,若
由此,我们想到把导弹的距离进行排序,按照每颗导弹距离一号系统的距离进行排序,排完序以后,我们可以近似的把每颗炮弹看成在一条线上。且都在系统一的某一侧,如下图 (其中橙色的点表示原炮弹所在位置,红色的表示将炮弹看成在一条线上的位置,绿色的表示系统)
于是,题目就变成了一个寻找分割位置的问题,我们可以枚举
此处有一个小技巧,关注到答案要求的是半径的平方和,又因为两个系统所用的代价只与其的半径有关,而与其内部的导弹距离无关,故我们可以在计算距离时,直接用平方进行计算,从而避免
时间复杂度:处理距离
以下是代码部分
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=(b);a<=(c);a++)
#define rap(a,b,c) for(int a=(b);a>=(c);a--)
#define pow2(x) ((x)*(x))
//带参数的宏定义最好给每个数字参数套上括号哦!
using namespace std;
const int MAXN=1e5+10;
int read()
{
int x=0,f=0;
char ch=0;
while(!isdigit(ch))f|=(ch=='-'),ch=getchar();
while(isdigit(ch))(x*=10)+=(ch^48),ch=getchar();
return f?-x:x;
}
//快读
struct Node
{
int dis1;
int dis2;
int x,y;
bool operator<(const Node &rhs)const
{
return dis1<rhs.dis1;
}
} node[MAXN];
//定义存储导弹信息的结构体,并重载运算符,按照距离一号系统的距离进行排序
int n;
int X1,Y1,X2,Y2;
//x1,y1,_x1,_y1属于系统保留的关键字,在Windows中可能不会报错,但在Linux中是肯定会CE的!
int ans=INT_MAX;
int main()
{
X1=read(),Y1=read();
X2=read(),Y2=read();
n=read();
rep(i,1,n)
node[i].x=read(),node[i].y=read();
//读入部分
rep(i,1,n)
{
node[i].dis1=pow2(X1-node[i].x)+pow2(Y1-node[i].y);
node[i].dis2=pow2(X2-node[i].x)+pow2(Y2-node[i].y);
}
//处理出导弹到每个系统的距离,为何不使用sqrt的原因上文已经阐述过了
sort(node+1,node+n+1);
//按照导弹到一号系统的距离排序
rap(i,n-1,1)
node[i].dis2=max(node[i].dis2,node[i+1].dis2);
//处理出后缀最大值
rep(i,1,n)
ans=min(ans,node[i].dis1+node[i+1].dis2);
//枚举分割点,把前i个交给一号系统,后面N-i个交给二号系统,由于我们在处理时就使用平方进行计算,故这里不需要在平方
printf("%d\n",ans);
return 0;//O=w=O
}