石子合并(区间dp入门题,记忆化搜索实现)

帖个题目链接
很经典的一道区间dp的题,用了记忆化搜索的方法实现.记忆化搜索实现的dp很多考虑细节会简单一点,主要因为我比较喜欢用自顶向下的方法思考问题,让我去扣递推的细节对我来说比较痛苦.
简单分析一下其实就能写出记忆化的代码,提供一下我的思路.
由最终状态分析,最后肯定是只有唯一的一堆石子,那么这堆石子是怎么合并出来的呢?可以选一个划分点,比如开始有八堆石子,这最后一堆可能是1-6和7-8合成的,也可能是1-5,6-8合成的,我们只需要选所有划分情况中代价最小的一种就可以了,假设目前的区间范围是i-j,划分点是k,转移方程也就是min(dp[i][j],dp[i][k]+dp[k+1][j]+合并这两个区间的代价),合并这两个区间的代价可以用前缀和来计算(随便贴一个牛客寒假训练营时候一个出题人写的关于前缀和以及差分的文章,写的挺好的),代价就是sum[j]-sum[i-1],我在代码里是只用了一个a数组存前缀和,多用一个sum数组也可以.
分析到现在其实程序的大体框架已经出来了,不过递归最重要的一个就是边界问题,这题的边界很简单,就是当i == j时代价就是0.
贴上code

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = 1e3+10;
const int INF = 0x3f3f3f3f;
const int AINF = 0x8f8f8f8f;
int a[N];
int dp[N][N];
int dfs(int i,int j){
	if(i == j) return dp[i][j] = 0;
	if(dp[i][j] != INF) return dp[i][j];
	int sum = a[j]-a[i-1];
	for(int k=i;k<j;++k){
		dp[i][j] = min(dp[i][j],dfs(i,k)+dfs(k+1,j)+sum);
	}
	return dp[i][j];
}
int main(){
	int n;
	cin >> n;
	for(int i=1;i<=n;++i){
		cin >> a[i];
		a[i] = a[i-1]+a[i];
	}
	memset(dp,0x3f,sizeof(dp));
	cout << dfs(1,n) << endl;
		
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45590210/article/details/104627540