P1158 导弹拦截

原题链接  https://www.luogu.org/problemnew/show/P1158

这个题耗了我好长时间呐QwQ~,看题解好多都是用的DP做,可是我这么蒟肯定是不会DP啦(以后要加强训练啊),所以用了暴力枚举+预处理过的此题!

直接说正解吧:

大体思路:

我们可以先让第一个拦截系统将所有导弹全部拦截,此时的答案就是第一个系统到最远的点的距离,然后依次去掉最远的导弹让第二个拦截系统拦截,同时注意取最小值,然后再输出最小值就好啦;

枚举之前我们可以将每个导弹按照距第一个拦截系统的距离从小到大排个序,这样我们枚举到 i 的话,那么比 i 小的也顺便给覆盖了,也就是1~i-1我们也顺便覆盖了,所以我们只算第 i 个导弹就行了; 

详细步骤:

1.按照每个导弹到第一个拦截系统的距离(x-x1)^2+(y-y1)^2从小到大排序;

2.然后我们枚举 i,i∈[1,n)(表示第一个拦截系统能覆盖多少个导弹,也可以理解为最多拦截 i 个导弹),那么此时第一个拦截系统肯定是正好拦截到距离它第 i 远的导弹(比它近的导弹一定也被拦截了),那么此时第一个拦截系统的半径就是距离它第 i 小的导弹的距离;我们只要再找剩下的导弹中距离第二个拦截系统最大的那个导弹的距离作为第二个导弹的半径就好啦;

例如:

此时第一个拦截系统拦截了6个导弹,那么第1~5枚导弹也一定被拦截了(我们已经将导弹按照距第一个拦截系统的距离从小到大排序了),那么此时第一个拦截系统的半径最小就是距离第 i 小的导弹的距离,那么第7,8小的导弹就由第二个拦截系统覆盖,我们再找这两个导弹中距离第二个拦截系统最大的那个作为第二个拦截系统的半径;

当然还有好多技巧都在代码里哦,结合代码认真分析吧:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int read()
{
    char ch=getchar();
    int a=0,x=1;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') x=-x;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        a=(a<<3)+(a<<1)+(ch-'0');
        ch=getchar();
    }
    return a*x;
}
int x1,y11,x2,y2,n,x,y,minx;
int dis2[100001],nxt[100001];      
/*
nxt[i]表示第i枚导弹到第n枚导弹的所有导弹中距离第二个拦截系统的最大距离;
也就是区间[i,n]中距离第二个拦截系统的最大距离,作为第二个拦截系统的半径 
*/
struct dis                                          //存导弹的信息 
{
    int dis1;
    int id;                                         //导弹的编号 
}a[100001];
int cmp1(dis x,dis y)
{
    return x.dis1<y.dis1;                           //从小到大 
}
int main()
{
    x1=read();y11=read();                           //第一个拦截系统的坐标 
    x2=read();y2=read();                            //第二个拦截系统的坐标 
    n=read();
    for(int i=1;i<=n;i++)
    {
        x=read();y=read();                          //每个导弹的坐标 
        a[i].dis1=(x-x1)*(x-x1)+(y-y11)*(y-y11);    //导弹距离第一个拦截系统的距离 
        a[i].id=i;                                  //第i个导弹 
        dis2[i]=(x-x2)*(x-x2)+(y-y2)*(y-y2);        //导弹距离第二个拦截系统的距离 
    }
    sort(a+1,a+1+n,cmp1);                           //将导弹按照距离第一个拦截系统的距离从小到大排序 
    minx=a[n].dis1;                                 //第一套系统全部拦截                   
    nxt[n]=dis2[a[n].id];                           //最后一颗导弹的nxt值就是它到第二个拦截系统的距离 
    for(int i=n-1;i>=1;i--)                         //预处理nxt的值 
       if(dis2[a[i].id]>nxt[i+1]) nxt[i]=dis2[a[i].id]; //如果当前导弹的距离比后面的区间的nxt大,那么加上这个导弹所组成的新区间的最大值就是当前导弹的距离 
          else nxt[i]=nxt[i+1];                     //如果不如后面的那个区间大,那就继承后面那个区间的最大值 
    for(int i=n-1;i>=1;i--)                         //第一个系统能拦截i个 
        minx=min(minx,a[i].dis1+nxt[i+1]);            
    /*
    a[i].dis1 就是第一个拦截系统的半径
    nxt[i+1]  就是第二个拦截系统的半径 (仔细想想,第一个系统已经拦了i个,后面的[i+1,n]需要第二个系统拦截,就是nxt[i+1] 
    */
    printf("%d",min(minx,nxt[1]));                   //nxt[1]就是第二个拦截系统全部拦截的情况 
    return 0;
}
原文地址:https://www.cnblogs.com/xcg123/p/11030193.html