算法题 - 数列最高得分

189 篇文章 3 订阅

问题描述:

给定一个非负数列,每次从中取出一个数字,而这个数字左右两边邻居值乘积作为本次取数的得分。被取出数字之后,剩余组成新的数列,且相对位置不变,如果最后这个数字没有左邻居或者右邻居,则补1即可。求,这些数字被取完,所有得分的总和最大是多少

例如下图


:这个题目是同学出的,传说是,三星实习生转正的测试题目,其实,如果没有左邻居或者右邻居是补 1, 还是补 0, 我不清楚,现在是按照补 1,计算。

问题分析:

(1)首先理解一个事情,就是把整个数列看做一个整体,那么最优解值,一定最后一个数字 i 的得分是1未知的),加上以 i 为分界线左右两个区间的最优解(有点像二分法)。这个不难理解,最后就剩下一个数字时,很显然,把两边的最优解加上最后一个 ,就是整体的最优解。这样一来就满足了动态规划的两个基本条件

        最优子结构:每个区间都符合这样的结构。

        重叠子问题:左右区间又生成左右区间,构成重叠问题

(2)问题:最后被取走的数字i 是哪一个?因为已经知道区间内的所有数字,那么就可以枚举遍历)这些数字,找到最优的那个 i 。这类问题一般称区间 dp 

(3)问题:左右小区间的最优解如何确定?那么现在就是,从最小区间开始计算,通俗的讲,先计算长度2的小区间,然后依次增加区间的长度。每次计算时,小区间就像一个滑动窗口,例如从长度为2的小区间作为一个窗口,从左向右依次滑动,求出所有小区间的最优解保存到dp[l][r]中。可以看出计算过程是自底向上的。所以dp[l][r]只用到了上三角的空间(l<r)。

(4)dp状态转换方程:

dp方程式为:dp[left][right] = max(dp[left][right], nums[left] * nums[right] + dp[left][i] + dp[i][right])

dp[left][right],表示区间 left-->right 的最优解(不包括,left 和 right 两个边界

nums[left] * nums[right],表示最后一个被取出的数字的得分,为什么要乘上两个边界值那?因为它是最后一个被取出来的,整体来看,最后就剩下一个,就是乘上两个1,在子区间内,就是乘上两边边界值了。

max(dp[left][right], nums[left] * nums[right] + dp[left][i] + dp[i][right]),表示从区间内开始枚举 i,计算出本次区间的最优解。

Python3实现(时间复杂度O(n3),空间复杂度O(n2)):

# @Time   :2018/7/14
# @Author :LiuYinxing
# 解题思路 区间动态规划

# 题目大意,给定一个数组,每次删除一个数字,并把这个数字相邻的两个数的积作为本次得分,如果左右没有相邻的数字,则补 1 。
# 求这个数列的最大得分是多少?


class Solution:
    def maxScore(self, Nums):
        nums = [1] + Nums + [1]  # 首尾添加[1],方便计算
        n = len(nums)
        dp = [[0] * n for _ in range(n)]  # 初始化dp
        for k in range(2, n):  # k 确定一个滑动窗口的大小,从2开始
            for left in range(0, n - k):  # 滑动窗口,从左向右滑动,确定区间的开始(left)、结束(right)位置
                right = left + k
                for i in range(left + 1, right):  # 开始枚举,区间内选择一个数字,并使其得分最高
                    dp[left][right] = max(dp[left][right],
                                          nums[left] * nums[right] + dp[left][i] + dp[i][right])
        return dp[0][n - 1]


if __name__ == '__main__':
    nums = [3, 1, 5, 8]
    solu = Solution()
    print(solu.maxScore(nums))
欢迎指正哦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值