区间环状dp-胖哥金项链

这篇文章介绍了一种使用动态规划解决金块项链问题的方法,通过合并权值,最大化项链的总价值。关键步骤包括将末尾视为开头,采用区间遍历和合并策略。

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

Description

有人说男人如果没有一条金项链就没有牌面, 我胖哥也有一条, 项链分为N段每一段都有自己的权值, L 为自己的权值, R为后面金块的权值, 我们可以将任意两个金块合并成一个, 最终我们需要把N个合并成一个, 每次合并会产生一定的价值, 合并时一定两两相邻, (第一视为和第N相邻); 
比如:A 与 A + 1 合并A : L1 = q【A】, R1 = q【A + 1】; 
                                B : L2 = q【A + 1】, R2 = q【A + 2】; 
会产生 L1 * R1 * R2 的价值, 生成一个权值为q【A】的金块 

Input

输入一个N, (4 <= N <= 100) 表示项链上的金块数 
读入N个数, 表示每一个金块的权值, 每个数0 < q【i】 <= 1000; 
第N块的后面视为第一块 

Output

打印胖哥项链最大的价值

Sample Input Copy

4
2 3 5 10

Sample Output Copy

710

HINT

4 和 1, 4 和 2, 4 和 3 
10 * 2 * 3 + 10 * 3 * 5  + 10 * 5 * 10 

题意:这个题可能是一个语文非常不好的人出的,反正我是没读懂,大致意思每次消掉一个数,即2*3*5消掉的就是3,然后最后只留下一个数;

思路:这样就构成一个环状,但是在代码里边环状很难实现,所以我们先将n个数复制到后面;然后再遍历;

有两种写法,其实思想是差不多的;

#include<bits/stdc++.h>
using namespace std;
long long a[1010],dp[1010][1010];
int main()
{
    long long n;
    while(~scanf("%lld",&n))
    {
        memset(dp,0,sizeof(dp));
        long long maxx=0;
        for(long long i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
            a[i+n]=a[i];
        }
        for(long long d=2; d<=n; d++)//这里就是开始的地方,也就是大区间;
            for(long long i=1; i+d<=2*n; i++)//小区间i->j
            {
                long long j=i+d;
                for(long long k=i; k<j; k++)//k就是划分界限,k+1就是要消掉的数;
                    dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+a[i]*a[k+1]*a[j+1]);
            }
            for(long long i=1;i<=n;i++)/这里max也可以在里边比较,因为在里边是2*n,所以慢了3s;
                maxx=max(maxx,dp[i][i+n-1]);//因为i是从1开始的,所以要 -1;
        printf("%lld\n",maxx);
    }
    return 0;
}

一定要开long long 不然WA的很惨;

### 环状动态规划简介 环状动态规划(环状DP)是一种特殊的动态规划问题,其特点是状态转移的边界条件形成了一个闭环。通常情况下,这类问题会涉及到一个首尾相连的序列或数组,例如一个环形数组或圆形排列的问题。由于环状结构的存在,传统的线性动态规划方法可能无法直接应用,因此需要对问题进行适当的变形和处理。 在解决环状动态规划问题时,常见的技巧是通过“拆环为链”的方式将环形结构转化为线性结构。具体来说,可以通过以下两种策略来处理环状动态规划问题: 1. 将环形数组复制一倍,形成一个两倍长度的线性数组。 2. 固定某个点作为起点或终点,然后在线性数组上进行动态规划。 以下是几个经典的环状动态规划问题及其解决方案。 --- ### 例子:房屋抢劫问题(House Robber II) #### 问题描述 在一个环形街道上有 `n` 座房子,每座房子内都藏有一定的现金。相邻的房子装有相互连通的防盗系统,如果两间相邻的房子同一天晚上被小偷闯入,系统会自动报警。给定一个非负整数数组 `nums` 表示每个房子内的现金金额,请计算在不触动警报装置的情况下,小偷一晚能够盗取的最高金额。 #### 解决方案 由于房子是环形排列的,不能同时抢劫第一间和最后一间房子。因此可以将问题分解为两个子问题: 1. 不抢劫第一间房子,即只考虑从第二间到最后一间房子。 2. 不抢劫最后一间房子,即只考虑从第一间到倒数第二间房子。 最终答案是这两个子问题的最大值。 ```python def rob(nums): if not nums: return 0 if len(nums) == 1: return nums[0] def helper(arr): n = len(arr) if n == 0: return 0 dp = [0] * n dp[0] = arr[0] dp[1] = max(arr[0], arr[1]) for i in range(2, n): dp[i] = max(dp[i-1], dp[i-2] + arr[i]) return dp[-1] # 分别计算两种情况 return max(helper(nums[1:]), helper(nums[:-1])) ``` --- ### 例子:最大子数组乘积(Maximum Product Subarray with Circular Condition) #### 问题描述 给定一个包含正数、负数和零的环形数组,找到其中连续子数组的最大乘积。注意,由于数组是环形的,首尾可以相连。 #### 解决方案 与上述房屋抢劫问题类似,可以通过将环形数组转化为两个线性数组来解决。首先计算普通线性数组的最大子数组乘积,然后计算去掉最小子数组后的剩余部分乘积,最终取两者中的较大值。 ```python def maxProductCircular(nums): def maxProductLinear(arr): if not arr: return 0 max_prod = min_prod = result = arr[0] for i in range(1, len(arr)): if arr[i] < 0: max_prod, min_prod = min_prod, max_prod max_prod = max(arr[i], max_prod * arr[i]) min_prod = min(arr[i], min_prod * arr[i]) result = max(result, max_prod) return result total = sum(nums) max_linear = maxProductLinear(nums) # 计算去掉最小子数组后的乘积 max_wrap = total - (-maxProductLinear([-x for x in nums[1:-1]])) return max(max_linear, max_wrap) if max_linear > 0 else max_linear ``` --- ### 例子:石子合并问题(Stone Merge Problem) #### 问题描述 在一个环形排列的石子堆中,每次可以选择相邻的两堆石子合并成一堆,并得到这两堆石子总数的分数。重复这一过程直到只剩下一堆石子为止。求出在所有可能的合并过程中,能得到的最小总分数。 #### 解决方案 可以通过动态规划解决该问题。定义 `dp[i][j]` 表示从第 `i` 堆到第 `j` 堆石子合并的最小分数。为了处理环形结构,可以将石子堆复制一倍,形成一个两倍长度的线性数组,然后在线性数组上进行动态规划。 ```python def mergeStones(rings, k): n = len(rings) if (n - 1) % (k - 1) != 0: return -1 # 无法完成合并 rings *= 2 prefix = [0] * (2 * n + 1) for i in range(2 * n): prefix[i+1] = prefix[i] + rings[i] INF = float('inf') dp = [[0] * (2 * n) for _ in range(2 * n)] for length in range(k, 2 * n): for i in range(2 * n - length): j = i + length - 1 dp[i][j] = INF for m in range(i, j, k-1): dp[i][j] = min(dp[i][j], dp[i][m] + dp[m+1][j]) if (j - i) % (k - 1) == 0: dp[i][j] += prefix[j+1] - prefix[i] result = min(dp[x][x+n-1] for x in range(n)) return result if result < INF else -1 ``` --- ### 总结 环状动态规划的核心在于如何将环形结构转化为线性结构,从而利用标准的动态规划算法解决问题。常用的技巧包括“拆环为链”和“固定起点/终点”。通过这些方法,可以有效地解决许多复杂的环状动态规划问题[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值