区间dp&记忆化搜索-P1063 能量项链

P1063 能量项链

https://www.luogu.org/problem/P1063
另外推荐一下同类型P1880 石子归并
思路
经典区间dp,题目要求的是整个区间的最优解,所以根据DPDP的最优性原理,我们可以从最小的可求最优解的区间入手(即n=2n=2,因为只有一颗珠子时无法合并),每次尝试把它分成两个不同的小区间,从中取最优的方案。

设f [ i ][ j ] ,f[ i ][ j ]为第 i 到第 j 颗珠子合并可得的最大值

动态转换方程:
f[ i ] [ j ] = max(f[ i ][ k ] + f [ k + 1][ j ] + a[ i ] * a[ k + 1 ] * a[ j + 1 ], f[ i ][ j ])
(两个区间之前合并的最大值+本次合并得分,枚举所有可能性取最优解)
嗯。。。题中还有一个重要的点,就是把环转化为直线,即为破坏环,可以发现将原数列复制一下接到后面就可以包含所有可能性了。

题解

#include<bits/stdc++.h>
using namespace std;
int main()
{
   	int n;
   	int a[300];
	int f[210][210];
	
	cin>>n;
	for(int i = 1; i <= n; i++)
	cin>>a[i], a[i + n] = a[i];  // 破坏环
	a[n + n + 1] = a[1]; 
	memset(f, 0, sizeof(f));
	for(int j = 1; j < n; j++)
	for(int i = 1; i <= 2*n-j+1; i++)  //i>n的部分也要dp出来,后续要用 
	{
		for(int k = i; k < i + j; k++)
		f[i][i + j] = max(f[i][i + j], f[i][k] + f[k + 1][i + j] + a[i] * a[k + 1] * a[i + j + 1]);
	 } 
       
	 int mx = 0;
	 for (int i = 1; i <= n; i++)
	 mx = max(mx, f[i][i + n - 1]);
	 cout<<mx<<endl;  
} 

记忆化搜索思路
很多时候记忆化搜索和dp有等同的思路和效果,比如这里。以每一个点为起点进行一次DP,记忆花搜索时间是没有问题的。然后其次就是递推。

f(l,r)=max(f(l,r),a[l]*a[i+1]*a[r+1]+f(l,i)+f(i+1,r));

记忆化搜索题解

#include<bits/stdc++.h>
using namespace std;
#define N 310
int f[N][N];
int a[N];
int n;
int dp(int l,int r){
    int ans=0;
    if(f[l][r])return f[l][r];  //记忆化剪枝
    if(l==r-1)return f[l][r]=a[l]*a[r]*a[r+1];
    for(int i=l;i<r;i++)
        ans=max(ans,a[l]*a[i+1]*a[r+1]+dp(l,i)+dp(i+1,r));      //dp转移
    return f[l][r]=ans;   // 记忆化
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i],a[n+i]=a[i];
    a[2*n+1]=a[1];
    int ans=0;
    for(int i=1;i<=n;i++)
        ans=max(ans,dp(i,i+n-1));  
    cout<<ans;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值