|
楼主 |
发表于 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] |
|