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;
}