题解:洛谷 P2677 Bookshelf 2

【题目来源】

洛谷:P2677 [USACO07DEC] Bookshelf 2 B - 洛谷

【题目描述】

Farmer John 最近为奶牛们的图书馆添置了一个巨大的书架,尽管它是如此的大,但它还是几乎瞬间就被各种各样的书塞满了。现在,只有书架的顶上还留有一点空间。所有 \(N(1\le N\le20)\) 头奶牛都有一个确定的身高 \(H_i(1\le H_i\le1,000,000\)(好高的奶牛 >_<)\()\)。设所有奶牛身高的和为 \(S\)。书架的高度为 \(B\),并且保证 \(1\le B\le S\)。为了够到比最高的那头奶牛还要高的书架顶,奶牛们不得不象演杂技一般,一头站在另一头的背上,叠成一座“奶牛塔”。当然,这个塔的高度,就是塔中所有奶牛的身高之和。为了往书架顶上放东西,所有奶牛的身高和必须不小于书架的高度。塔叠得越高便越不稳定,于是奶牛们希望找到一种方案,使得叠出的塔在高度不小于书架高度的情况下,高度尽可能小。你也可以猜到你的任务了:写一个程序,计算奶牛们叠成的塔在满足要求的情况下,最少要比书架高多少。

【输入】

\(1\) 行:\(2\) 个用空格隔开的整数:\(N\)\(B\)

\(2 \sim N+1\) 行:第 \(i+1\) 行是 \(1\) 个整数:\(H_i\)

【输出】

\(1\) 行:输出 \(1\) 个非负整数,即奶牛们叠成的塔最少比书架高的高度。

【输入样例】

5 16
3
1
3
5
6

【输出样例】

1

【核心思想】

  1. 问题分析:给定 \(N\) 头奶牛的身高 \(H_i\) 和书架高度 \(B\),要求从 \(N\) 头奶牛中选出一个子集,使其身高之和 \(\ge B\),且与 \(B\) 的差值最小。这是一个子集枚举问题,关键在于遍历所有可能的选取方案,找到满足条件的最小差值。由于 \(N \le 20\),总子集数为 \(2^{20} \approx 10^6\),适合用 DFS(深度优先搜索) 枚举。

  2. 算法选择

    • DFS 枚举子集:对每头奶牛进行"选"或"不选"的二元决策,递归遍历所有 \(2^N\) 种组合
    • 剪枝优化:在递归到底时计算当前子集的和,仅当 \(\text{sum} \ge B\) 时更新答案
  3. 关键步骤

    • 初始化:读取 \(N\)(奶牛数量)、\(B\)(书架高度),以及 \(H[1..N]\)(每头奶牛的身高)
    • DFS 递归(参数 \(\text{step}\) 表示当前处理第 \(\text{step}\) 头奶牛):
      • 边界条件:若 \(\text{step} = N + 1\),表示所有奶牛已决策完毕:
        • 计算当前选中奶牛的身高总和 \(\text{sum} = \sum_{i=1}^{N} (\text{used}[i] \times H_i)\)
        • \(\text{sum} \ge B\),更新最小差值:\(\text{ans} = \min(\text{ans}, \text{sum} - B)\)
        • 返回
      • 不选第 \(\text{step}\) 头奶牛\(\text{used}[\text{step}] = \text{false}\),递归 \(\text{dfs}(\text{step} + 1)\)
      • 还原现场\(\text{used}[\text{step}] = \text{false}\)
      • 选第 \(\text{step}\) 头奶牛\(\text{used}[\text{step}] = \text{true}\),递归 \(\text{dfs}(\text{step} + 1)\)
      • 还原现场\(\text{used}[\text{step}] = \text{false}\)
    • 输出\(\text{ans}\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(2^N \times N)\),共 \(2^N\) 个子集,每个子集求和需要 \(O(N)\)
    • 空间复杂度:\(O(N)\),递归栈深度为 \(N\),加上 \(\text{used}\) 数组
  5. DFS 枚举子集的核心思想

    • 二元决策树:每头奶牛对应一个"选/不选"的分支,形成深度为 \(N\) 的满二叉树,遍历所有叶子节点即遍历所有子集
    • 回溯与还原现场:每次递归后恢复状态,确保不同分支之间的决策互不干扰
    • 边界处理:到达叶子节点时统一计算并判断,避免中途剪枝导致遗漏
    • 数据范围适配\(N \le 20\)\(2^N\) 可接受;若 \(N\) 更大需改用 01 背包 等动态规划优化
    • 适用于数据范围较小、需要枚举所有组合方案的子集选择类问题

【解题思路】

【算法标签】

普及- #DFS-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, b, h[25], used[25], ans=1e9;  // 定义used数组,标记某头奶牛选或不选
void dfs(int step)
{
    if (step==n+1) {  // 当n头牛都选完了
        int sum=0;  // 定义奶牛们的身高总和
        for (int i=1; i<=n; i++) {  // 遍历n头奶牛
            if (used[i]==true) {  // 对于选中的奶牛
                sum += h[i];  // 计算身高总和
            }
        }
        if (sum-b>=0) ans = min(ans, sum-b);  // 在身高总和与b的差值大于等于0的基础上,计算最小值
        return;  // !!!!一定要写!!!
    }
    used[step]=false;  // 不选该头牛
    dfs(step+1);  // 继续深搜
    used[step]=false;  // 还原现场
    used[step]=true;  // 选择该头牛
    dfs(step+1);  // 继续深搜
    used[step]=false;  // 还原现场
}
int main()
{
    cin >> n >> b;  // 输入n和b
    for (int i=1; i<=n; i++) {  // 输入n头奶牛的高度
        cin >> h[i];
    }
    dfs(1);  // 开始深搜
    cout << ans << endl;  // 输出结果
    return 0;
}

【运行结果】

5 16
3
1
3
5
6
1
posted @ 2026-08-17 09:46  团爸讲算法  阅读(8)  评论(0)    收藏  举报