理解区间dp和例题

一、讲解
1、作用
用于不知道从哪里合并的动态规划题,不比线性dp
2、解法步骤
即枚举区间长度,再枚举左端点,之后枚举区间的断点进行转移。
3、核心思路
既然让我求解在一个区间上的最优解,那么我把这个区间分割成一个个小区间,
求解每个小区间的最优解,再合并小区间得到大区间即可。所以在代码实现上,
我可以枚举区间长度len为每次分割成的小区间长度(由短到长不断合并),
内层枚举该长度下可以的起点,自然终点也就明了了。然后在这个起点终点之间枚举分割点,
求解这段小区间在某个分割点下的最优解。
4、图解法
在这里插入图片描述
二、石子合并直线型
1、代码

#include<stdio.h>
#include<string.h>
int min(int a,int b){
	if(a>b)
	return b;
	else
	return a;
}
int main(){
	//dp[i][j]表示从第i堆石头合并到第j堆石头最小得分(从问题出发,缩小规模) 
	int dp[105][105];
	//sum[i]表示前i堆石头的数量总和 
	int sum[105];
	int n;
	int i,j,k;
	int l;
	scanf("%d",&n);
	sum[0]=0;
	for(i=1;i<=n;i++){
		scanf("%d",&j);
//		因为要求解区间和,先维护前缀后 
		sum[i]=sum[i-1]+j;
	}
//	初始为0,dp[i][i]=0 
	memset(dp,0,sizeof(dp));
//	枚举区间长度,从两个区间开始 
	for(l=2;l<=n;l++){
//		i=n-1+1,因为初始要l个,之后都是要一个一个的,算个数要+ ,枚举左端 
		for(i=1;i<=n-l+1;i++){
//			右端点,因为包含i,故减1 
			j=i+l-1;
//			if(j>n)=
//			break;
//			初始化最大值, 
		dp[i][j]=0x3f3f3f3f;
//			举区间的断点进行转移
			for(k=i;k<j;k++){
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
			}
		}
	}
//	for(i=1;i<=n;i++){
//		for(j=1;j<=n;j++)
//		printf("%d ",dp[i][j]);
//		printf("\n");
   printf("%d",dp[1][n]);
}

2、sum与dp数组
sum是用来求两个区间合并所需要花费的钱,而dp是一个区间的已经花费的钱

参考资料:
区间dp入门
算法学习之区间dp

三、能量项链环型
1、石子合并的sum是多变的,而这道题sum用head代替了,状态转移方程也变化了,主要看sum或者head,其他方面都没改变
2、环变成直线的方法
本题由于是环,还需破环为列,可以开两倍大的数组,即a[i]=a[i+n],便可在第n颗珠子时求到第1颗珠子的头标记(也即第n颗珠子的尾标记)
3、代码

#include<stdio.h>
#include<string.h>
int max(int x,int y){
	if(x>y)
	return x;
	else
	return y;
}
int main(){
   int dp[205][205];
   int head[105];
   int n;
   int i,j,k,l;
   int ans=0;
   	scanf("%d",&n);
   	for(i=1;i<=n;i++){
   		scanf("%d",&head[i]);
   		head[n+i]=head[i];
	}
	
	for(l=2;l<=n;l++){
		//把环变成列,故有2n个 
		for(i=1;i<=n*2-l+1;i++){
			j=i+l-1;
			for(k=i;k<j;k++){
				//head[i]*head[j+1]*head[k+1],例如,和合并第一个和第二个(1,2)(2,4),
				//第一个的值为1,第二个的值为2,第三个的值为4,i=1,k=1,j=2,故用head[i]*head[j+1]*head[k+1] 
				dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+head[i]*head[j+1]*head[k+1]);
			}
		}
	}
	for(i=1;i<=n;i++){
		ans=max(ans,dp[i][i+n-1]);
	} 
	printf("%d",ans);
} 

参考资料
能量项链 (区间DP)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值