♐ CSP_J 组真题
2010年
3. 导弹拦截

线上OJ:

一本通:http://ybt.ssoier.cn:8088/problem_show.php?pid=1951 (opens in a new tab)
AcWing:https://www.acwing.com/problem/content/description/445/ (opens in a new tab)
洛谷:https://www.luogu.com.cn/problem/P1158 (opens in a new tab)

核心思想:

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

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

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

如果a[i]是1#拦截系统的最大范围,a[j]是2#拦截系统的最大范围,(由于拦截半径的平方和就是最远导弹和拦截系统的距离的平方和),所以
1#拦截系统工作半径的平方和: r12=a[i].d1=(a[i].xx1)2+(a[i].yy1)2r1^2 = a[i].d1 = (a[i].x− x1)^2+(a[i].y−y1)^2
2#拦截系统工作半径的平方和: r22=a[j].d2=(a[j].xx1)2+(a[j].yy1)2r2^2 = a[j].d2 = (a[j].x− x1)^2+(a[j].y−y1)^2
故,本题所求的其实是在导弹被全覆盖情况下 a[i].d1+a[j].d2a[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;
}