思路
首先需要明确几个关键概念:
- 圆包含原点的判定:若圆心
(x,y)到原点的距离d = √(x²+y²)小于半径r,则该圆包含原点;否则不包含。 - 移动代价计算:代价 = 圆面积 × 移动距离。圆面积为
πr²,移动距离是让圆不再包含原点所需的最小距离。 - 最小代价策略:对于原本包含原点的圆,若需要让它不包含原点,只需将其向任意方向移动
(r - d)的距离(这是最小移动距离);对于原本不包含原点的圆,无需移动。
要让总代价最小,需遵循 “最小代价优先保留” 原则:
- 先筛选出所有原本包含原点的圆,计算每个圆被移出原点的代价
(r-d)×πr²; - 将这些代价从大到小排序;
- 保留代价最大的
k个圆(即不移动这k个圆,让它们继续包含原点),移动剩余的圆(代价累加),最终总代价即为最小值。
代码
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll = long long;
#define all(a) a.begin(), a.end()
const int M = 1e5 + 3;
const int mod = 80112002;
const double pi = acos(-1.0); // 高精度π值
void solve()
{
int n, k;
cin >> n >> k;
vector<double> arr; // 存储需要移动的圆的代价
double sum = 0; // 总代价
for (int i = 1; i <= n; i++)
{
double x, y, r;
cin >> x >> y >> r;
// 计算圆心到原点的距离d
double d = sqrt(x * x + y * y);
// 若圆包含原点,计算移出代价并加入列表
if (d < r)
arr.push_back((r - d) * pi * r * r);
}
//代价从大到小排序,保留前k个(不移动),移动剩余的
sort(all(arr), greater<double>());
// 累加第k个及之后的代价(需要移动的圆的总代价)
for (int i = k; i < arr.size(); i++)
sum += arr[i];
printf("%.8lf\n", sum);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int qwq = 1;
while (qwq--)
{
solve();
}
return 0;
}
复杂度分析
- 时间复杂度:O (n log n)(主要来自排序);
- 空间复杂度:O (n)(存储代价列表)。

京公网安备 11010502036488号