【题目链接】
ybt 1951:【10NOIP普及组】导弹拦截
洛谷 P1158 [NOIP 2010 普及组] 导弹拦截
【题目考点】
1. 预处理+枚举
【解题思路】
第一个拦截系统的坐标为 P 1 P_1 P1,第二个拦截系统的坐标为 P 2 P_2 P2。
拦截系统可以拦截的范围是以拦截系统所在坐标为圆心的一个圆。
第一个拦截系统的拦截范围称为"第一个圆",第二个拦截系统的拦截范围称为"第二个圆。导弹所在位置为多个点。
题目所求为:使得两圆包含所有给定点的前提下,两圆半径的平方和最小。
思路上相当于不断扩大第一个圆半径,第一个圆包含了一些点,第二个圆就要包含剩下的点。
d i s ( i , j ) dis(i,j) dis(i,j)表示 i 、 j i、j i、j两点之间距离的平方。
a a a序列保存所有点,将 a a a序列按照到 P 1 P_1 P1的距离从小到大进行排序。
d p 1 i dp1_i dp1i表示第一个圆包含前 i i i个点时,半径的平方。
- 初值: d p 1 0 = 0 dp1_0=0 dp10=0
- 前 i i i个点中,距离 P 1 P_1 P1最远的点为 a i a_i ai,因此: d p 1 i = d i s ( a i , P 1 ) dp1_i=dis(a_i, P_1) dp1i=dis(ai,P1)。
d p 2 i dp2_i dp2i表示第二个圆包含第 i i i到第 n n n个点时,半径的平方。
- 初值: d p 2 n + 1 = 0 dp2_{n+1}=0 dp2n+1=0
- 第 i i i到第 n n n个点中距离 P 2 P_2 P2最远的点,可能是第 i + 1 i+1 i+1到第 n n n个点钟距离 P 2 P_2 P2最远的点,其距离的平方为 d p 2 i + 1 dp2_{i+1} dp2i+1;也可能是第 i i i个点,其距离的平方为 d i s ( a i , P 2 ) dis(a_i,P_2) dis(ai,P2),二者取较大值。因此: d p 2 i = m a x ( d p 2 i + 1 , d i s ( a i , P 2 ) ) dp2_i=max(dp2_{i+1}, dis(a_i, P_2)) dp2i=max(dp2i+1,dis(ai,P2))
枚举分割点 i i i,第1到第 i i i点被第一个圆包含,第 i + 1 i+1 i+1到第 n n n个点被第二个圆包含。 i i i最小为0,即第二个圆包含所有点。 i i i最大为 n n n,即第一个圆包含所有点。
当分割点为 i i i时,此时的代价为第一个圆的半径的平方 d p 1 i dp1_i dp1i与第二个圆半径的平方 d p 2 i + 1 dp2_{i+1} dp2i+1的加和: d p 1 i + d p 2 i + 1 dp1_i+dp2_{i+1} dp1i+dp2i+1,求所有出现的该值的最小值,即为本题的结果。
相同的思路,也可以变递推为迭代。
【题解代码】
解法1:预处理+枚举
- 写法1:递推
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
struct Pair
{
int x, y;
} p1, p2, a[N];
int dp1[N], dp2[N];
int disSquare(Pair a, Pair b)//距离的平方
{
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
bool cmp(Pair a, Pair b)
{
return disSquare(a, p1) < disSquare(b, p1);
}
int main()
{
int n, ans = 1e9;
cin >> p1.x >> p1.y >> p2.x >> p2.y >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i].x >> a[i].y;
sort(a+1, a+1+n, cmp);
for(int i = 1; i <= n; ++i)
dp1[i] = disSquare(a[i], p1);//dp1[i]:a[1]~a[i]各点到p1的最大距离
for(int i = n; i >= 1; --i)
dp2[i] = max(dp2[i+1], disSquare(a[i], p2));//dp2[i]:a[i]~a[n]各点到p2的最大距离
for(int i = 0; i <= n; ++i)//0~i归系统1 i+1~n归系统2
ans = min(ans, dp1[i]+dp2[i+1]);
cout << ans;
return 0;
}
- 写法2:迭代
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
struct Pair
{
int x, y;
} p1, p2, a[N];
int disSquare(Pair a, Pair b)//距离的平方
{
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
bool cmp(Pair a, Pair b)
{
return disSquare(a, p1) < disSquare(b, p1);
}
int main()
{
int n, ans = 1e9, r1, r2 = 0;//r1, r2:系统半径的平方
cin >> p1.x >> p1.y >> p2.x >> p2.y >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i].x >> a[i].y;
sort(a+1, a+1+n, cmp);
for(int i = n; i >= 1; --i)//1~i归系统1 i+1~n归系统2
{
r1 = disSquare(a[i], p1);
ans = min(ans, r1+r2);
r2 = max(r2, disSquare(a[i], p2));
}
cout << min(ans, r2);//考虑全部由系统2拦截的情况
return 0;
}