题解:学而思编程 修建支柱

【题目来源】

学而思编程:修建支柱

【题目描述】

小明要建造一个台子,现在还缺两根支柱,目前有高度不同的 \(n\) 个支柱可以选择,第 \(i\) 个支柱的高度为 \(h_i\),华丽度为 \(v_i\)。多个支柱可以拼接成一根支柱,拼接后的高度等于参与拼接的支柱高度之和。

小明打算从 \(n\) 个支柱中选若干支柱,拼成两个高度相同的支柱。他只关心支柱的华丽度,求出能拼成两根高度相同的支柱的前提下,两根支柱华丽度总和的最大值。

华丽度有可能是负数,如果不论如何选,总华丽度都是负数,那么小明宁可不做支柱。

【输入】

\(1\) 行,\(1\) 个正整数 \(n\)

\(2\) 行,\(n\) 个正整数 \(h_1,h_2,⋯ ,h_n\)

\(3\) 行,\(n\) 个整数 \(v_1,v_2,⋯ ,v_n\)

【输出】

一个非负整数,输出两根支柱华丽度总和的最大值。

【输入样例】

5
1 3 3 4 7
3 3 1 4 1

【输出样例】

10

【核心思想】

  1. 问题分析:给定 \(n\) 个支柱,每个支柱有高度 \(h_i\) 和华丽度 \(v_i\)。需要选择若干支柱拼成两根高度相同的支柱,最大化两根支柱的华丽度总和。这是一个天平平衡问题,关键在于将支柱分配到两根支柱上,使得高度差为0。每个支柱有三种选择:不选、放在第一根支柱(增加高度差)、放在第二根支柱(减少高度差)。

  2. 算法选择

    • 带偏移量的二维DPdp[i][j] 表示前 \(i\) 个支柱,当前高度差为 \(j\) 时的最大华丽度总和
    • 偏移量处理:高度差可能为负数,使用偏移量 \(p = 60000\) 将范围 \([-p, p]\) 映射到数组下标 \([0, 2p]\)
  3. 关键步骤

    • 状态初始化dp[0][p] = 0(0个支柱时高度差为0),其余初始化为负无穷
    • 状态转移:对于第 \(i\) 个支柱
      • 不选:dp[i][j] = dp[i-1][j]
      • 放第一根(增加高度差 \(h_i\)):dp[i][j] = max(dp[i][j], dp[i-1][j-h[i]] + v[i])(要求 \(j \geq h_i\)
      • 放第二根(减少高度差 \(h_i\)):dp[i][j] = max(dp[i][j], dp[i-1][j+h[i]] + v[i])(要求 \(j + h_i \leq 2p\)
    • 答案获取dp[n][p] 即高度差为0时的最大华丽度总和(若结果为负数则输出0)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \times p)\),其中 \(p = 60000\) 为最大可能的高度差
    • 空间复杂度:\(O(n \times p)\),二维DP数组
  5. 带偏移量的多维选择DP的核心思想

    • 问题转化:将"拼成两根高度相同的支柱"转化为"使高度差为0"
    • 三状态转移:每个物品三种选择(不选、放左边、放右边),类似01背包的扩展
    • 偏移量技巧:将可能为负的高度差通过加偏移量映射到非负数组下标
    • 目标约束:最终必须回到平衡状态(高度差为0)
    • 适用于"将物品分配到两个集合使某种度量相等"的背包问题

【算法标签】

01背包

【代码详解】

#include<iostream>
#include<cstring>
using namespace std;
const int p = 60000, N = 125;  // p: 偏移量,用于处理负数下标
int n, h[N], v[N], dp[N][2 * p];  // dp[i][j]: 前i个砝码,平衡度为j时的最大价值

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> h[i];  // 砝码重量
    }
    for (int i = 1; i <= n; i++)
    {
        cin >> v[i];  // 砝码价值
    }

    memset(dp, -0x3f, sizeof(dp));  // 初始化为负无穷
    dp[0][p] = 0;  // 初始状态:0个砝码,平衡度为0(加上偏移量p)

    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= 2 * p; j++)
        {
            // 不选第i个砝码
            dp[i][j] = dp[i - 1][j];
            // 将砝码放在左边(增加平衡度)
            if (j >= h[i])
            {
                dp[i][j] = max(dp[i][j], dp[i - 1][j - h[i]] + v[i]);
            }
            // 将砝码放在右边(减少平衡度)
            if (j + h[i] <= 2 * p)
            {
                dp[i][j] = max(dp[i][j], dp[i - 1][j + h[i]] + v[i]);
            }
        }
    }
    cout << dp[n][p];  // 输出平衡度为0时的最大价值
    return 0;
}

【运行结果】

5
1 3 3 4 7
3 3 1 4 1
10
posted @ 2026-06-14 10:45  团爸讲算法  阅读(7)  评论(0)    收藏  举报