题目如上
题解:
题目理解
我们有 n 把三角尺,每把尺子有两条直角边 x 和 y。其中 y 边可以打磨(缩短),打磨总量不能超过 w。打磨后斜边长度会变小,目标是让所有尺子斜边长度总和最小。
思路 1. 问题转化
每把尺子的斜边长度公式:
斜边 = √(x² + y²)
打磨就是减小 y,从而减小斜边长度。
关键观察:随着打磨量增加,斜边长度减少的幅度(边际收益)会越来越小。
2. 贪心策略
想象一下:
初始时,每把尺子都没打磨
我们有 w 次"打磨机会",每次可以选一把尺子打磨 1 单位
每次应该选择打磨后斜边缩短最多的那把尺子
3. 实现方法
用优先队列(大根堆)来维护:
堆里放 (当前打磨收益, 尺子编号)
收益 = 现在打磨 1 单位能减少的斜边长度
每次弹出收益最大的尺子打磨
打磨后重新计算这把尺子的新收益,放回堆里
AC码如下(AI辅助完成,非完全独立完成):
void solve() {
int n, w, i;
cin >> n >> w;
vi a(n), b(n), nw(n);
ld sum = 0;
priority_queue<pair<ld, int>> pq;
for (i = 0; i < n; i++) {
cin >> a[i] >> b[i];
nw[i] = b[i];
sum += sqrtl((ld)a[i] * a[i] + (ld)b[i] * b[i]);
if (b[i] > 0) {
ld cur = sqrtl((ld)a[i] * a[i] + (ld)b[i] * b[i]);
ld next = sqrtl((ld)a[i] * a[i] + (ld)(b[i] - 1) * (b[i] - 1));
ld gain = cur - next;
pq.push({gain, i});
}
}
ll cnt = 0;
while (!pq.empty() && cnt < w) {
auto [gain, idx] = pq.top();
pq.pop();
cnt++;
sum -= gain;
nw[idx]--;
if (nw[idx] > 0) {
ld cur = sqrtl((ld)a[idx] * a[idx] + (ld)nw[idx] * nw[idx]);
ld next = sqrtl((ld)a[idx] * a[idx] + (ld)(nw[idx] - 1) * (nw[idx] - 1));
ld new_gain = cur - next;
pq.push({new_gain, idx});
}
}
cout << fixed << setprecision(10) << sum;
}

京公网安备 11010502036488号