poj1651/zoj1602 Multiplication Puzzle(区间dp入门)

题目

给你一个序列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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值