P5909 挂缀pendant 题解

P5909 挂缀pendant 题解

是一道经典的反悔贪心题(不太理解反悔贪心可以先看文章末尾),可以学到一个证明方法,更模板的题:P4053 建筑抢修

题意简述

给定 \(n\) 个珠缀,并且每个限定 \(c\)\(w\) 表示珠缀的称重能力与重量。

每个珠缀能承受的重量是其下方所有珠缀的总和,不包括自己,求最长可以挂多长的珠缀,与最小总重量。

思考过程

首先我们发现,如果是正向模拟(从第一个依次往下挂),每挂一个就要去维护以上的所有信息,肯定是 \(O(n^2)\) 时间的,所以我们正难则反,考虑一下从末尾向上挂,发现可以。

之后看出来这个是一种类似 “带截止时间的任务选择问题” ,与建筑抢修很像,所以考虑用反悔贪心。

这道题不一样的点在哪呢,在于对每一项的排序,因为这个挂缀的限制是对于不包含自己的下方总重量的限制,所以我们需要考虑什么情况是更优的。

我们去设想一种最优情况,挂了 \(i+1\) 再挂 \(i\) 可以,挂了 \(i\) 不能挂 \(i+1\) ,所以列出式子:

\[SUM+W_i\ge C_{i+1} \]

表示之前的 \(sum\) 加上新的挂缀 \(w_i\) 是要比第 \(i+1\) 个的重量限制要大的。

\[SUM+W_{i+1}< C_{i} \]

表示之前的 \(sum\) 加上新的挂缀 \(w_{i+1}\) 是要比第 \(i\) 个的重量限制小的。

变形得到:

\[C_{i+1}-W_i\le SUM < C_i-W_{i+1}\\ C_{i+1}-W_i\le C_i-W_{i+1}\\ C_{i+1}+W_{i+1} < C_i+W_i \]

也就是说,最优情况是在 \(C_{i+1}+W_{i+1} < C_i+W_i\) 时成立,所以我们证明到,需要按照 \(C+W\) 的大小作为权值去进行排序。

然后对于反悔贪心的部分,我们去定义一个动态维护当下总重量的变量,如果其满足即将进来的新珠子的 \(C\) 的要求,就可以直接加入并且放入队列,否则我们就需要去从优先队列里寻找最大的一个数,去与这个新珠子做比较,然后抉择放入哪个,如果队列中的大,就去替换(因为前面的所有珠子不会因为重量变小而导致超重,并且这样可以使总重量变小,所以这是一个合法贪心)。

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5*2+100;
int n;
struct node {
    ll c, w;
}s[N];
//通过证明需要由w+c作为排序标准
bool cmp(node a, node b) {
    return a.w+a.c<b.w+b.c;
}
//这个队列用来记录被选进来的重量
priority_queue<int> q;
int main() {
    cin>>n;
    for (int i=1; i<=n; i++) {
        cin>>s[i].c>>s[i].w;
    }
    sort(s+1,s+1+n,cmp);
    //发现如果正的从上往下去吊会用n^2,正难则反,尝试从末尾往回去吊
    ll sum=0, L=0;
    for (int i=1; i<=n; i++) {
        //如果之前的sum符合当下的限制,就可以直接加入新珠子
        if (sum<=s[i].c) {
            L++;
            sum+=s[i].w;
            q.push(s[i].w);
        }else {
            //否则我们需要去看一下以前加入的最重的珠子,看看能否替换
            if (q.top()>s[i].w) {
                sum-=q.top();//总重量减去之前的最大
                q.pop();
                q.push(s[i].w);
                sum+=s[i].w;//加入现在最大的
            }
        }
    }
    cout<<L<<'\n'<<sum;
    return 0;
}

关于反悔贪心

反悔贪心,字面意思就是可以反悔的贪心,也就是在遇见一个更优的选项,我们会去反悔之前的某个选项。

这道题我们使用优先队列去记录选择了的珠子的重量,在遇见一个新的珠子的时候,我们就可以去看一下之前选的最劣的与现在选的这个哪个更优,然后就可以去做反悔操作。

有一个比较形象的比喻:反悔贪心就是在吃自助餐,先去把所有能吃的吃了,在吃撑的时候,吐出来一个价值最低的食物,吃价值更高的食物(大卫·戴 [edge] )。

结语

首先我们可以学习到反悔贪心的思想,然后还学会了一种贪心的证明方法:通过假设一个最优可能,去看一下最优策略之间选项的关系,然后可以选择排序规则。

喜欢点个赞吧,这是我的第一篇题解,谢谢(*^▽^*)

posted @ 2026-01-27 08:55  Yuriha  阅读(15)  评论(0)    收藏  举报