洛谷-P5194 [USACO05DEC] Scales S 题解

题目链接:https://www.luogu.com.cn/problem/P5194

题目大意

给定一个天平,最大承重为 C。有 N 个砝码,质量按非降序排列,并且从第 3 个开始,每个砝码的质量 至少等于前两个砝码质量之和(即满足类似斐波那契的性质)。要求选出若干砝码,使得它们的总质量 不超过 C,并且尽可能大。

换句话说:在满足总质量 ≤ C 的前提下,求能选出的最大砝码质量和。

关键观察

1. 砝码序列具有“快速增长”特性

题目中给出一个重要条件:

从第 3 个砝码开始,每个砝码 ≥ 前两个砝码之和。

这意味着砝码序列增长非常快(至少是斐波那契级别),因此:

  • 即使 N = 1000,实际有效的砝码数量也不会太多(因为很快就会超过 C ≤ 2^30)。
  • 更重要的是,这个性质保证了贪心或剪枝搜索的高效性

2. 暴力枚举不可行,但可以 DFS + 剪枝

直接枚举所有子集(2^N)显然不行(N=1000 太大)。但由于砝码增长快,实际递归深度很小,配合前缀和剪枝,可以高效解决。

解法思路:DFS + 前缀和优化剪枝

我们从最大的砝码开始考虑(倒序处理),对每个砝码有两种选择:

  • 不选它;
  • 选它(前提是加上它不会超过 C)。

为了加速,预处理前缀和 presum[i] = nums[0] + ... + nums[i]

剪枝策略

在递归函数 process(index, sum) 中(表示当前考虑第 index 个砝码,已选砝码总质量为 sum):

  1. 如果 sum + presum[index] <= C→ 说明从 0 到 index 的所有砝码都可以选,直接取满,更新答案并返回。
  2. 否则:先尝试不选当前砝码:process(index - 1, sum)再尝试选当前砝码(如果 sum + nums[index] <= C):process(index - 1, sum + nums[index])

由于砝码增长快,presum[index] 很快会远大于 C,所以递归树非常浅,效率很高。

正确性证明(关键)

为什么这个 DFS 能找到最优解?

  • 我们遍历了所有可能的合法组合(通过回溯)。
  • 剪枝 if (presum[index] + sum <= c) 是安全的:因为如果剩余所有砝码都能加进去还不超限,那肯定是最优的(加得越多越好),无需再分叉。
  • 由于砝码非负,目标是最大化总和,所以“能全拿就全拿”是正确的贪心选择。

此外,题目中砝码满足“类斐波那契”增长,保证了 presum 增长极快,使得剪枝极其有效,实际运行时间接近 O(N) 或 O(log C)。

代码解析

#include<iostream>
using namespace std;

long long nums[1001], presum[1001];
long long c, n, ans;

void process(int index, long long sum) {
    if (index < 0) {
        ans = max(ans, sum);
        return;
    }
    // 强剪枝:如果剩下的全部加上也不超 C,直接拿完
    if (presum[index] + sum <= c) {
        ans = max(ans, presum[index] + sum);
        return;
    }
    // 不选当前砝码
    process(index - 1, sum);
    // 选当前砝码(如果可以)
    if (sum + nums[index] <= c) {
        process(index - 1, sum + nums[index]);
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> c;
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }
    // 计算前缀和
    presum[0] = nums[0];
    for (int i = 1; i < n; i++) 
        presum[i] = presum[i - 1] + nums[i];
    
    ans = 0;
    process(n - 1, 0); // 从最后一个砝码开始
    cout << ans;
    return 0;
}

注意点

  • 使用 long long 防止溢出(虽然 C ≤ 2^30,但砝码值可能是 32 位有符号整数)。
  • 前缀和 presum[i] 表示前 i+1 个砝码的总和(0-indexed)。
  • 递归从大到小处理,便于剪枝。

复杂度分析

  • 时间复杂度:看似 O(2^N),但由于砝码快速增长 + 强剪枝,实际复杂度约为 O(N)O(log C)。例如,斐波那契数列到第 45 项就超过 2^30,所以 N 实际有效长度 ≤ 45。
  • 空间复杂度:O(N),用于存储砝码和前缀和,递归栈深度也很小。

总结

本题利用了砝码序列的快速增长性质,通过 DFS + 前缀和剪枝 实现高效搜索。核心在于:

  • 从大到小考虑砝码;
  • 若剩余砝码全选不超限,则直接取满(最优);
  • 否则分“选/不选”两种情况递归。

这种“类斐波那契”约束是本题的关键突破口,使得指数级问题变为线性可解。

全部评论
还是第一次看到这个题目的平台
点赞 回复 分享
发布于 12-24 21:11 北京
我每次是遇到这类题就头大
点赞 回复 分享
发布于 12-24 18:58 陕西

相关推荐

12-19 02:15
门头沟学院 C++
1.&nbsp;实习介绍2.&nbsp;两段开源经历拷打,主要聊开发过程遇到的事,技术涉及较少,虽然也没什么技术,估计就是确认一下是本人干的。3.&nbsp;面试官介绍自己部门不是搞数据库内核的,询问真想来吗,给面试官给予了肯定的回答。4.&nbsp;开发习惯闲聊,看不看火焰图,跨语言的benchmark怎么测的巴拉巴拉。5.&nbsp;正式开始拷打,汗流浃背了。简历上项目就是常规15445+tinykv,遇到一个也都做过的面试官相当正常。6.&nbsp;15445&nbsp;lru-k算法、crabbing&nbsp;协议(还包括读写锁细节,楼主都快记不得了,头一次有面试官问这个)。7.&nbsp;ACID&nbsp;含义(楼主顺便聊了一下CAP的C跟ACID的C区别,直接预判面试官)8.&nbsp;15445&nbsp;三种隔离级别(RU,&nbsp;RC,&nbsp;RR,这块楼主早忘记了,所以回答的是mysql和pg的实现细节,参考:https://gg2002.github.io/2025/03/16/mysql-latch,顺便扯了几嘴mysql为啥会有表级锁和binlog,因为mysql是一个分离式的架构巴拉巴拉)9.&nbsp;tinykv拷打,multi&nbsp;raft必要性,项目思想。10.&nbsp;分布式事务Percolator跟寻常单体数据库事务的差别(楼主大败而归,说到3列,但是忘记怎么具体地写这3列)11.&nbsp;raft全流程介绍(leaderelection+logreplication,楼主顺便加了点行业现状试图展示知识面)12.&nbsp;raft脑裂问题,prevote优化介绍13.&nbsp;raft的Leader&nbsp;Lease和ReadIndex优化(更是大败而归,头一次有面试官问这个,早就忘记了,扯了几嘴思想草草而过)14.&nbsp;面试官询问tinysql,楼主没做过,但楼主打过ob数据库比赛,说那个比赛sql写的多,再次跟面试官闲聊一阵15.&nbsp;广告场景题,问楼主广告曝光log和点击log哪个存kv好些,楼主说点击log少些,存点击,面试官说错,然后解释16.&nbsp;算法题,线程安全的LRU
点赞 评论 收藏
分享
12-27 16:21
已编辑
门头沟学院 Java
bg:中下211本科,java后端,无竞赛,无基础,大一升大二暑假开始学java。五段实习:美团-小红书-腾讯-淘天-字节。面秋招的简历只有美团、小红书、淘天。刚刚发现我的秋招蚂蚁流程挂了,这是我最后一个流程,那么我的秋招就算彻底结束了,总结一下:字节ssp+,职级2-1。美团ssp,+2打了半小时微信电话极力挽留。快手ssp,但报了字节薪资后没有争取的想法了。小红书sp,今年小红书给的很高,但比字节2-1还是差很多。虾皮应该是小sp?对虾皮一点意向都没,纯拿来集邮了。淘天ssp(暑期转正),说不要我的三方,毕业前考虑好了随时可以不签三方选择淘天。挂了的流程:京东二面挂,估计学历被卡了。懂车帝一面挂,和面试官聊不来,不认同我的方案。拼多多hr面挂,问我低于预期还来不来,当时说不考虑了,估计觉得我不忠诚。蚂蚁hr面挂,聊的还行,但估计我不会去给我挂了吧。阿里控股一面挂,没面前就知道是kpi了,因为时间可选的很多,而且都是半小时,我也拿他刷我的kpi了。上面差不多是我的情况,下面是我想说的话。我觉得我不算特别突出优秀的那类人,但我多少也算是靠前的那一批人,即使这样,秋招也不算特别顺利,也有挂了的流程,但你能说是我的问题吗,我觉得大部分情况不是的,如果真的是我的问题,我不可能本科校招拿到2-1,所以很多面试挂了,问题不出在面试者身上,很多是看运气+眼缘+和面试官合不合得来。所以我觉得,学会察言观色,了解面试官的脾性,也是面试很重要的一个点。比如面试官是喜欢听长回答,还是听短回答,他更看重哪些点,每个面试官对这些的侧重都是不一样的,所以作为面试者,要学会察言观色,通过面试官开局的一两个问题以及你回答后他的表现,就要判断出来。像我现在其实面试开局个五分钟,我就基本能判断个七七八八了,然后我后面的回答就会有所变化。这是我想说的第一个点:不要为面试结果焦虑,有时候问题不出在你身上,但你可以学一些面试技巧,尽量提高你的面试通过率,这里说的面试技巧指的不是网上那种烂大街的,一两分钟短视频说什么提高你面试通过率的,而是你要在你自己的面试过程中不断总结经验,吸取教训,旁人教你的终究是有限的。另外想说下选offer的事,上面其实可以看出来,我秋招最后是选了字节的,还没签三方我就来提前实习感受业务了,当我签完三方又过了一个多月,我这些天又在想这个问题,字节真的是我想要的吗,我现在总结了一下字节的好坏,发现当时可能被字节的高薪资影响判断了,如果现在再选一次的话,我应该会选杭州的小红书,会生活的更舒服点。具体种种就不展开说了。然后虽然我现在也可以说去把小红书舔回来,去毁字节,但我觉得没必要这么做,我可以采用其他的措施去不就,比如规划好两年内就跳槽,跳到杭州,跳到更舒适的城市。我觉得大家选offer的时候,真的可以冷静下来多方面考虑,薪资、城市、组内氛围、业务、老板是否看重、组内情况、未来升职机会等等都是可以考虑的因素,虽然有的时候不管选哪个,都不会坏,但最好也别让自己后悔吧,即使真后悔了,我觉得也没必要过度美化没走过的路,想好补救措施即可。这是我想说的第二个点:冷静好好做选择,不管是offer还是其他。但人生容错率很大,即使选错了,也一定有补救措施。最后还想说一些成长上的东西,尤其是现在AI火热的时代。我觉得大家如果想提高自己,或者说在未来社招跳槽有竞争力,肯定是要学AI相关的东西的,不说要会多懂AI,至少也要了解基本概念,而且一定要学会用AI提效。我现在字节的mt和我说,他现在80%代码都是AI写的。而我最近也开始尝试用AI工具,感觉现在AI真的进步很多,挺聪明的了,我现在写需求基本都是先让AI写,我再人工review小改动一下就差不多了。我觉得「AI取代程序员」是个很远的话题,但是「AI取代不会用AI的程序员」,可能真的就是近两年的事了。而怎么去学习这块的内容,其实我也正在探索,我也是刚学AI的起步阶段,我觉得大家也要有自己的信息检索能力,而不是别人喂你什么,你才学什么,自己一个人就不会学了。这是我想说的第三个点:趁年轻,多学习提升自己,拥抱AI,不要原地踏步,原地踏步的程序员最容易被淘汰。大概就是这样吧,今天看蚂蚁流程发现挂了,前几天腾讯约面我也拒了,就想到自己的秋招/校招算彻底结束了,有感而发,随便聊了下。牛客以后应该不会更新,大家不用关注,熟悉我的朋友应该知道我在其他平台有号。我更喜欢以长视频的形式去做分享,感觉会更有体系,而不是网上那种一两分钟的零碎短视频的那种营销号去起号,我也推荐大家多去看高质量的长文章、长视频,我觉得收获的能更多。希望大家能收获满意的offer与未来。
兑生:都这么疯狂了,毁字节去小红书也挺好
2025年终总结
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务