CSP历年复赛题-P1158 [NOIP2010 普及组] 导弹拦截
原题链接:https://www.luogu.com.cn/problem/P1158
题意解读:用两套拦截系统拦截全部导弹,计算最小的拦截系统半径平方和。
解题思路:
错误的思路:
枚举每一个导弹,计算到两套系统的距离,距离谁近就归属哪套系统管。
错误原因:
举个反例,假设只有两个导弹,
第一个导弹,距离两个系统s1,s2,s1 < s2,归属到系统1,系统1的半径至少是s1
第二个导弹,距离两个系统d1,d2,d2 < d1,d1 < s1,
如果归属到系统2,则系统2的半径至少是d2,这样结果是s1 + d2
而如果归属到系统1,则系统2的半径可以是0,系统1的半径最小仍是s1,这样结果是s1,显然结果更小。
正确的思路:
先将所有导弹都归属给系统1,这样系统1的半径至少是所有导弹中距离系统1最远的
再逐步缩小系统1的半径,漏出的导弹归属给系统2,每漏出一个,更新一次系统1的最小半径,系统2的更小半径,再计算一次结果,取最小值
编程实现时,
可以先将所有导弹与系统1、系统2的距离都计算出来,再按照与系统1的距离从大到小排序
枚举每一个导弹的作为漏出系统1,归属给系统2的,更新枚举过程中的系统1、系统2最小半径,计算结果,取最小值
注意:以上提到的距离、半径都是平方之后的结果。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
struct node
{
int s1, s2;
};
node w[N];
int X1, Y1, X2, Y2, n;
bool cmp(node a, node b)
{
return a.s1 > b.s1;
}
int main()
{
cin >> X1 >> Y1 >> X2 >> Y2 >> n;
int x, y;
for(int i = 1; i <= n; i++)
{
cin >> x >> y;
w[i].s1 = (x - X1) * (x - X1) + (y - Y1) * (y - Y1);
w[i].s2 = (x - X2) * (x - X2) + (y - Y2) * (y - Y2);
}
sort(w + 1, w + n + 1, cmp);
int r1 = 0; //拦截系统1的最小半径
int r2 = 0; //拦截系统2的最小半径
int ans = INT_MAX; //r1 + r2
for(int i = 0; i <= n; i++) //注意从0开始可以处理一开始全归属系统1
{
r1 = w[i+1].s1; //把i归属给拦截系统2,拦截系统1的半径更新
r2 = max(r2, w[i].s2); //拦截系统2的最小半径更新,每次归属的导弹取较大值
ans = min(ans, r1 + r2);
}
cout << ans;
return 0;
}