|

楼主 |
发表于 2021-6-20 09:26:23
|
显示全部楼层
最佳答案过不了
记忆化递归搜索方式解决:根据题意,首先要清楚,本题无法通过贪心的方式解答。
最后一次合并,两颗珠子合并成一颗珠子,共n种情况,珠子呈环状结构。即第1种情况,从1号珠子开始,连续的n个珠子合并,第2种情况,从2号珠子开始,连续的n个珠子合并,。。。。。,第n种情况,从n号珠子开始,连续n个珠子合并。最终需要从这n种情况中选择最大总能量。因为能量珠连成环,处理不方便,所以把它展开成链。把原先的n个能量珠扩成2n-1个,第i个和第i+n个相同。总能量ans可以表示成
ans=max(f(1,n),f(2,n+1),f(3,n+2),…….f(n,2n-1)),其中f(1,n)表示从1号珠子开始到n号珠子结束,能获得的最大能量。
对于f(1,n),是最终由两颗珠子合并而成,若干种情况,如,1,2--n;1--2,3--n;。。。。。。1--n-1,n,从这若干种情况中选择能量最大的
求解过程可以表示成
Ans=max(f(1,1)+f(2,n)+ head[1]*tail[1]*tail[n], f(1,2)+f(3,n)+ head[1]*tail[2]*tail[n],。。。。。,f(1,n-1)+f(n,n)+ head[1]*tail[n-1]*tail[n])
对于f(1,1),f(2,n) 。。。。。。。f(1,n-1),f(n,n),同理可求
由此可见,可以通过递归搜索的方式解决问题。
f(i,j)
{
ans=0;
for(int k=i;k<j;k++)
ans=max(ans,f(i,k)+f(k+1,j)+ head[i]*tail[k]*tail[j])
}
边界条件 f(k,k)=0
通过简单递归搜索,很可能超时,仔细观察发现,里面有很多小段的珠子能量
会重复计算合并,本题需采用记忆化递归搜索。
动态规划方式解决:
对于区间[i,j]合并成的能量珠,它一定是由两个能量珠合并而来,我们假设它是由[i,k]和[k+1,j]合并成的。
记dp[i][j]为合并成[i,j]获得的最大能量,我们枚举断点k,那么我们很容易得出状态转移方程
dp[i][j]=max(dp[i][k]+dp[k+1][j]+head[i]*tail[k]*tail[j]),其中head为头标记,tail为尾标记。
因为能量珠连成环,处理不方便,所以把它展开成链。
我们把原先的n个能量珠扩成2n-1个,第i个和第i+n个相同,我们只需要进行DP后找出max(dp[i][i+n-1])就是答案。
枚举合并的次数t,枚举i,那么j就是i+n,枚举k,进行转移即可。
时间复杂度O(n^3)。
- [code]#include<cstdio>
- #include<iostream>
- #include<algorithm>
- #include<cstring>
- #include<cmath>
- #define nn 2*n-1
- using namespace std;
- int dp[500][500],head[500],tail[500],n,ans;
- int main()
- {
- cin>>n;
- for(int i=1;i<=n;i++){
- scanf("%d",&head[i]);
- head[n+i]=head[i];
- }
- for(int i=1;i<=nn;i++) tail[i]=head[i%n+1];
- for(int i=1;i<=nn;i++) dp[i][i]=0;
- for(int t=1;t<n;t++){
- for(int i=1;i<=nn;i++){
- int j=i+t;
- for(int k=i;k<j;k++) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+head[i]*tail[k]*tail[j]);
- }
- }
- for(int i=1;i<=nn;i++) ans=max(ans,dp[i][i+n-1]);
- cout<<ans;
-
- return 0;
- }
复制代码
[/code] |
|