能量项链 环状DP模板题目

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=210;
//环状DP
ll dp[N][N];
int a[N],n;//开二倍数组
int main()
{
		cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];	//初始化
		a[n+i]=a[i];//倍增数组 i和1要分清
	}
	for(int len=2;len<=n;len++)	
  //区间长度len
	//左端点l
	//右端点r
	{
		for(int l=1;l+len-1<2*n;l++)//约束区间起始点
		{
			int r =l+len-1;
			for(int k=l;k<r;k++)//设置断点
			{//同所有的情况进行比较
			//原来的能量+新产生的能量
				dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r]+a[l]*a[k+1]*a[r+1]);
			}
		}
	}
	//对每一个点全部截断取最值
	ll ans=0;
	for(int i=1;i<=n;i++) ans=max(ans,dp[i][i+n-1]);
	cout<<ans<<endl;
	return 0;
}
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int N=110;
int n,a[N<<1];
ll dp[N<<1][N<<1];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) a[i+n]=a[i];
	for(int l=2;l<=n;l++)//区间长度小于3的时候不执行没有意义 
		for(int i=1;(i+l-1)<=(2*n-1);i++)
		{//保证右端点的范围
    //i+l-1表示区间的右端点
    //2n是复制之后最后一个珠子的索引
    //2n-1是因为不用遍历到复制之后的最后一个珠子
    //n-2n和1-n没有区别
			int j=i+l-1;//定义右端点
			for(int k=i;k<j;k++)//断点从左端点开始小于右端点
				dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+1ll*a[i]*a[k+1]*a[j+1]);
    //dp[i][k]表示合并区间[i,j]能够获得的最大分数
		//a[i]是第i-1颗珠子的为
		}    
		//2  3  5  10
		/*若 
		i指向2 
		k指向3 
		j指向10
		那么1ll*a[i]*a[k+1]*a[j+1]就是2*5*2
		此时表示将A(2 3)和B(5 10)进行合并,A合并之后的头为2 尾标记为5 B合并之后头标记为5 尾标记为2(10是最后一颗珠子,首尾相连) 
		*/
	ll ans=0;
	for(int i=1;i<=n;i++) ans=max(ans,dp[i][i+n-1]);
	printf("%lld\n",ans);
	return 0;
}

### 环状动态规划简介 环状动态规划(环状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、付费专栏及课程。

余额充值