2010NOIP普及组真题 3. 导弹拦截

线上OJ:

一本通:http://ybt.ssoier.cn:8088/problem_show.php?pid=1951

核心思想:

1、我们把导弹分为区间1和区间2来看。1#拦截区间1,2#拦截区间2
2、则:1#的拦截半径为区间1最远的导弹,而2#的拦截半径为 区间2最远 的导弹。
3、所以本题可以 枚举所有区间 的可能性,并计算每种区间划分下的 r 1 2 + r 2 2 r1^2+ r2^2 r12+r22
4、最后求出极小值.
5、枚举区间之前,我们先考虑区间1包含了所有导弹,区间2一个都不包含
6、然后把 区间1 里面的导弹按照 从远到近的顺序 一个一个漏出去,每放出去一个,更新一次 r 1 2 r1^2 r12
7、区间2 则考虑 囊括漏出来的导弹,如果需要更新拦截半径才能囊括,则更新 r 2 2 r2^2 r22

假设某导弹 a[i]的坐标为(ax, ay);

1# 拦截系统坐标为(x1, y1);
2# 拦截系统坐标为(x2, y2);
则 a[i]到两个防御系统的“距离的平方和”分别为
d 1 = ( a x − x 1 ) 2 + ( a y − y 1 ) 2 d1 = (ax− x1)^2+(ay−y1)^2 d1=(axx1)2+(ayy1)2
d 2 = ( a x − x 2 ) 2 + ( a y − y 2 ) 2 d2 = (ax− x2)^2+(ay−y2)^2 d2=(axx2)2+(ayy2)2

如果a[i]是1#拦截系统的最大范围,a[j]是2#拦截系统的最大范围,(由于拦截半径的平方和就是最远导弹和拦截系统的距离的平方和),所以
1#拦截系统工作半径的平方和: r 1 2 = a [ i ] . d 1 = ( a [ i ] . x − x 1 ) 2 + ( a [ i ] . y − y 1 ) 2 r1^2 = a[i].d1 = (a[i].x− x1)^2+(a[i].y−y1)^2 r12=a[i].d1=(a[i].xx1)2+(a[i].yy1)2
2#拦截系统工作半径的平方和: r 2 2 = a [ j ] . d 2 = ( a [ j ] . x − x 1 ) 2 + ( a [ j ] . y − y 1 ) 2 r2^2 = a[j].d2 = (a[j].x− x1)^2+(a[j].y−y1)^2 r22=a[j].d2=(a[j].xx1)2+(a[j].yy1)2
故,本题所求的其实是在导弹被全覆盖情况下 a [ i ] . d 1 + a [ j ] . d 2 a[i].d1 + a[j].d2 a[i].d1+a[j].d2 的最小值。
具体理解可参见下图

在这里插入图片描述

题解代码:
#include <bits/stdc++.h>
#define INF 1e9
using namespace std;

const int N = 100005;

struct Node
{
    int d1, d2;  // d1和d2 分别记录该导弹到1#和2#拦截系统的“距离的平方和”;
};
Node a[N];  // a[i]表示第i个导弹

bool cmp(Node a, Node b)  // 先根据导弹与1#拦截系统的距离进行升序排序
{
    return a.d1 < b.d1;
}

int main()
{
    int x1, y1, x2, y2, n;
    scanf("%d %d %d %d %d", &x1, &y1, &x2, &y2, &n);

    for(int i = 1; i <= n; i++) // 读入每个导弹的坐标,并计算导弹与1#和2#拦截系统的“距离的平方和”
    {
        int ax, ay;
        scanf("%d %d", &ax, &ay);
        a[i].d1 = (ax - x1)*(ax - x1) + (ay - y1)*(ay - y1);
        a[i].d2 = (ax - x2)*(ax - x2) + (ay - y2)*(ay - y2);
    }

    sort(a + 1, a + 1 + n, cmp); // 根据导弹与1#拦截系统的距离进行升序排序

	// 从“1#拦截所有,2#拦截0个”开始枚举
    int r22 = 0;       // r22表示r2^2,记录2#拦截系统最大工作半径的平方和。由于初始2#拦截0个,所以r22初始化为0
    int ans = a[n].d1; // 记录 r1^2 + r2^2 的最小值。 r1^2就是a[i].d1,r2^2就是r22。由于初始时1#拦截所有,所以a[n].d1即为结果
    for(int i = n; i >= 1; i--) // 2#拦截系统专拦截1#的漏网之鱼,1#每漏掉一个,2#就要重新计算最大工作半径的平方和,将其包含进去。
    {
        r22 = max(r22, a[i].d2);
        ans = min(ans, a[i-1].d1 + r22);  // a[0].d1 = 0
    }

    printf("%d\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值