题解:学而思编程 修建支柱
【题目来源】
学而思编程:修建支柱
【题目描述】
小明要建造一个台子,现在还缺两根支柱,目前有高度不同的 \(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
【核心思想】
-
问题分析:给定 \(n\) 个支柱,每个支柱有高度 \(h_i\) 和华丽度 \(v_i\)。需要选择若干支柱拼成两根高度相同的支柱,最大化两根支柱的华丽度总和。这是一个天平平衡问题,关键在于将支柱分配到两根支柱上,使得高度差为0。每个支柱有三种选择:不选、放在第一根支柱(增加高度差)、放在第二根支柱(减少高度差)。
-
算法选择:
- 带偏移量的二维DP:
dp[i][j]表示前 \(i\) 个支柱,当前高度差为 \(j\) 时的最大华丽度总和 - 偏移量处理:高度差可能为负数,使用偏移量 \(p = 60000\) 将范围 \([-p, p]\) 映射到数组下标 \([0, 2p]\)
- 带偏移量的二维DP:
-
关键步骤:
- 状态初始化:
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)
- 状态初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(n \times p)\),其中 \(p = 60000\) 为最大可能的高度差
- 空间复杂度:\(O(n \times p)\),二维DP数组
-
带偏移量的多维选择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
浙公网安备 33010602011771号