题目
给你一个序列a[],两端的数不能进行操作
剩下的可以被选中,
每次选中会导致这个数和它左右两个数相乘成为一个新的数,
cost+=新的数
然后问怎么选能使这个序列最后只剩下两个端点的时候cost最小,
输出这个cost
思路来源
https://www.cnblogs.com/hoodlum1980/archive/2012/06/07/2540150.html
题解
这段序列肯定得有一个数最后一个合,
不妨这个数位置在k,那么应该是合并完了[0,k][k,n-1]这两个区间
再来合并0,k,n-1这三个点所致,
所以枚举这个k,使得cost答案最小
那[0,k][k,n-1]又从何而知呢?
可以记忆化搜索,这样我们就把大问题变成了小问题
dp具有最优子结构的性质,所以就变成了解决一个短区间的问题
来源于一个很直观的结果,小的相乘还小,小的相加也小
肯定是小的+小的转移+小*小*小出来小的
既然有一组是最小的,那么这么转移肯定能把那组挑出来
而注意到仅有3个点的时候,只能枚举中间那个点,
所以有dp[i][j]=min(dp[i][k]+dp[k][j]+a[i]*a[k]*a[j],dp[i][j])成立,
转移的时候,认为子区间已经被处理好,只剩两个端点
注意初始化,即仅2个点的时候,没有cost,默认赋0即可
心得
和思路来源有同感
明明就差那么一点
可是就是做不出来
看了一下题解就会
手敲下代码也不难
然而就是需要补题
还是做题量不够吧
专题还是要多做吖
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<algorithm>
typedef long long ll;
using namespace std;
const int maxn=105;
ll dp[maxn][maxn],n,a[maxn];
void solve()
{
//dp[i][i+1]=0 已经没法合了
for(int len=3;len<=n;len++)//枚举长度
{
for(int i=0;i+len-1<n;++i)//枚举左端点
{
dp[i][i+len-1]=1e18;
for(int k=i+1;k<i+len-1;++k)//枚举中间点
{//把左区间合的只剩i和k,右区间合的只剩k和i+len-1,再合这三个
dp[i][i+len-1]=min(dp[i][k]+dp[k][i+len-1]+a[i]*a[k]*a[i+len-1],dp[i][i+len-1]);//划分为子问题 优先把左右区间合了 即剩下两个端点
}
}
}
}
int main()
{
scanf("%lld",&n);
for(int i=0;i<n;++i)scanf("%lld",&a[i]);
solve();
printf("%lld\n",dp[0][n-1]);
return 0;
}