一、题目描述
题目链接:https://leetcode-cn.com/problems/tallest-billboard/
二、题目分析
本题被力扣归为动态规划,无奈本人水平实在有限,最终只能用搜索+剪枝解决。
首先我们有一个最直观的想法,广告牌有left和right两根柱子,对于题目给的钢筋,我们可以选择放在左边,可以可选择放在右边,也可以选择都不放。由此我们便有了回溯的一个基本思想。
然而若我们不对直接进行回溯而不剪枝,是过不了的。因此我们要考虑剪枝的条件。我们将左边的钢筋高度(累计)记为left,右边的钢筋高度(累计)记为right,再使用一个int类型的remain来记录剩余的钢筋的累计和,使用一个ans来记录当前广告牌可达到的最大值。当我们遇到以下两种情况其实就可以返回了。
1、abs(left - right)> remain,即就算剩下的所有的钢筋给短的那边也不够高度。
2、left + right + remain <= 2 * ans,即就算加上remain之后left == right,我们也抛弃,因为左边三者的总和的一半也比当前可达到的最大值小了。
而为了能让我们的剪枝条件生效,我们应该尽量从大的钢筋开始处理。(这里是一个比较直观的想法,并没有进行证明,若由大佬知道如何证明还望评论区指教)
三、代码
private int ans = 0;
public int tallestBillboard(int[] rods) {
int len = rods.length;
if (len == 0 || len == 1) return 0;
// 计算钢筋的总和
int remain = 0;
for (int i: rods) {
remain += i;
}
Arrays.sort(rods);
// 从最大的钢筋开始处理
billBoardHelper(rods,rods.length-1,remain,0,0);
return ans;
}
private void billBoardHelper(int[] rods, int i, int remain, int left, int right) {
// 若左右相等,则进行比较
if (left == right && left > ans) {
ans = left;
}
// 剪枝
if (Math.abs(left - right) > remain || left + right + remain <= ans * 2 || i < 0) return;
// 开始对第i根钢筋进行处理,remain减去rods[i]
remain -= rods[i];
// 放左边
billBoardHelper(rods,i-1,remain,left + rods[i],right);
// 放右边
billBoardHelper(rods,i-1,remain,left,right + rods[i]);
// 不放
billBoardHelper(rods,i-1,remain,left,right);
}