LeetCode 312: Brust Balloon 解题与思考

本文详细解析了LeetCode 312气球爆破问题,通过转变思路,将其转化为动态规划问题,介绍了如何通过递推公式找到最优解的过程。文章还提供了完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LeetCode 312: Brust Balloon 解题与思考

[原题链接]

题目描述

题目会输入一个长度为n的数列 an (n从0开始算),你需要从中按照一定顺序取出所有的数字;每次取出一个数字 ak 时,你将得到该数字与其左右相邻数字的乘积的分数 ak1akak+1
几点注意:

  • 最左边的数的左边,以及最右边的数的右边视为1( a1=an=1 )
  • 取出之后这个数会从原数列消除,这个数的左右两个数将会变得相邻

题目要求求出对于给出的任意序列所能得到的得分最大值。

思路:

我最开始直观地感觉是这是一道动态规划的题目,因为假设我们取出某个数,这个问题就变成为:求剩下的数列中所能取得的最大值,是原问题的一个类似子问题

我们也可以得到一个关系式:

=max(+)

但是很快就遇到了如下问题:

  • 取出一个数的得分和其左右两个数相关,而这两个数原则上可以是剩下的左边和右边的任意某个数(当然也能是1)
  • 这个数被取走后,数列会被分成两半,而这两半独自的最大分数,与将这两个数列看做一个新数列所能取得的最大分数,并不存在关系:因为取走某个数之后,相邻关系会改变,左边的数可能会与右边的任意数相邻,反之亦然;之前求得的最大值毫无意义

但是动态规划的这个方向应该还是没问题的
所以接下来的工作重心,就在于

  • 如何确定取走某个数之后所能得到的确切分数
  • 如何建立原数列最大分数与新数列分数的关系

突破口在于,当数列中只有一个数,或者被取到只剩一个数(a[k])时,我们(理所当然地)知道最后我们能拿到这个数所得到的分数,为

1ak1

或者说:
a1akan

而且 取走这个数左边的数所能得到的最大分数,与取走这个数右边的数所能取得的最大分数相互无关,因为a[k]是最后取走的,此时左边的数列永远不会与右边的数相邻。
而此时,我们看左边的数列, 其最左边的数的左边是1,最右边的数的右边是a[k];而对于最右边的数列, 其最左边的数的左边是a[k],最右边的数的右边是1。倘若a[k] == 1,不难发现其和最初的数列类似,可以视作一个子问题处理。

算法

假设 ansp,q 代表数列中从第p项开始,包括该项在内的接下来q项的所构成新数列,所能取得的最大分数
ak 是该数列中最后取走的数,那么

ansp,q=max(ap1apap+q+ansp,kp+ansk+1,p+qk1)

其中

1、 pk<p+q
2、 ap1 为数列最左边的数左边的数, ap+q 为数列最右边的数右边的数,这两个数在操作这个数列的时候不会被取走,也不会有变化
3、 ansp,kp ap1 ak 之间的数列所能取得的最大分数, ansk+1,p+qk1 ak+1 ap+q 之间的数列所能取得的最大分数
4、 ansp,0=0
5、 ansp,1=ap1apap+1
6、 ans0,n 为所求解

代码

#include <iostream>
#include <stdlib.h>
#include <vector>
#include <stdio.h>
using namespace std;

class Solution {
    //取最大值函数
    int max(int a, int b) {
        return (a > b) ? a : b;
    }
public:
    int maxCoins(vector<int>& nums) {
        int i, j, k;
        int length = nums.size();
        //新建数组并初始化为0
        int *ans = (int*)(malloc(sizeof(int) * (length + 1) * (length + 1)));
        memset((void*)ans, 0, sizeof(int) * (length + 1) * (length + 1));
        //动态规划,三层循环
        for ( i = 1; i < length + 1; i++ ) {
            for ( j = 0; j < length - i + 1; j++ ) {
                for ( k = j; k < j + i; k++ ) {
                    ans[j * length + i] = max(nums.at(k) * ((j <= 0) ? 1 : nums.at(j - 1)) * ((j + i >= length)?1:nums.at(j + i)) + ans[j * length + k - j] + ans[(k + 1) * length + j + i - k - 1], ans[j * length + i]);
                }
            }
        }
        //好习惯,申请的空间要释放
        int result = ans[length];
        free(ans);
        //返回结果
        return result;
    }
};

思考

很有趣的一道题目,关键点在于从每次取走一个数,变成取走的是最后一个数,这一思维上的转变——而后者是能够轻松动态规划的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值