动态规划之——区间DP

参考链接:https://blog.csdn.net/my_sunshine26/article/details/77141398

一、算法梗概

区间DP是一种在一个线性的空间上求得最优解的规划过程,某些问题是非线性的但仍可以转化为线性求解(例如:一个环形的数据可以将其数据量*2后首尾连接在一起求解);

二、具体思路

比较常见的思路是一种O(n3)级别的求解过程具体思路如下图代码所示:

void init()//初始化

void DP():算法实现模板

第一种为较流行的模板,理解难度大;

第二种为自己实现的模板,比较便于理解(弱鸡的自我救赎)

void init(){
    dp[i][j]=Max+sum(i,j);
}
void DP(){
	for(int len=2;len<=n;len++){
		for(int i=1,j=i+len-1;j<=n;i++,j++){//i:start  j:end
			dp[i][j]=-INF;
			for(int k=i;k<j;k++){//k:i...j
				dp[i][j]=max(Max,dp[i][k]+dp[k+1][j]+sum(i,j));
			}
		}
	}
}
void DP(){
    for(int len=2;len<=n;len++){
		for(int i=1,j=i+len-1;j<=n;i++,j++){//i:start  j:end
			int Max=-INF;
			for(int k=i;k<j;k++){//k:i...j
			    Max=max(Max,dp[i][k]+dp[k+1][j]);
			}
			dp[i][j]=Max+sum(i,j);
		}
	}
}

1、模板解释

一层for:控制遍历的区间长度,因为len=1时已经初始化,所以从len=2遍历到len=n;

二层for:定义i,j分别为区间长度为len下的开头下标与结尾下标;

三层for:定义k,表示的是当前遍历的区间的划分点

例如:当i=1,j=4时,k to:1...3三个划分点分别将区间[1,4]划分为

[1,1] + [2,4]

[1,2] + [3,4]

[1,3] + [4,4]

三个区间

遍历后即可获得最优解

2、模板注意事项(第二种DP模板)

1.在二层for中对每个小区间dp前,应该对大区间dp[i][j]进行初始化,若不初始化,默认值为0可能造成无法更新dp[i][j]

2.因为sum(i,j)在dp时每个小区间是都会+的,所以不妨单独提出来最后加,更便于理解而已

三、相关例题

1、洛谷 P1880 [NOI1995]石子合并

链接:https://www.luogu.org/problemnew/show/P1880

AC代码:

#include<iostream>
#define MAX_N 205
#define INF 0x3f3f3f3f
using namespace std;
int n;
int Data[MAX_N];
int dpmin[MAX_N][MAX_N];
int dpmax[MAX_N][MAX_N];
int Sum[MAX_N];
int sum(int x,int y) {
	return Sum[y]-Sum[x-1];
}
void input() {
	cin>>n;
	for(int i=1; i<=n*2; i++) {
		if(i<=n)
			cin>>Data[i];
		Data[i+n]=Data[i];
		Sum[i]=Sum[i-1]+Data[i];
	}
	n*=2;
}
void init() {
	for(int i=1; i<=n; i++) {
		dpmin[i][i]=0;
		dpmax[i][i]=0;
	}
}
void Dp() {
	for(int len=2; len<=n/2; len++) {
		for(int i=1,j=i+len-1; j<=n; i++,j++) { //i:start  j:end
			int Min=INF;
			int Max=-INF;
			for(int k=i; k<j; k++) { //k:i...j
				Min=min(Min,dpmin[i][k]+dpmin[k+1][j]);
				Max=max(Max,dpmax[i][k]+dpmax[k+1][j]);
//				cout<<sum(i,j)<<endl;
			}
			dpmin[i][j]=Min+sum(i,j);
			dpmax[i][j]=Max+sum(i,j);
		}
	}
}
void output() {
	int ans1=INF,ans2=-INF;

	for(int i=1,j=n/2 ; j<=n ; i++,j++ ) {
		ans1=min(ans1,dpmin[i][j]);
		ans2=max(ans2,dpmax[i][j]);
	}
	cout<<ans1<<endl<<ans2<<endl;
//	cout<<endl;
//	for(int i=1;i<=n;i++){
//		for(int j=1;j<=n;j++){
//			cout<<dpmin[i][j]<<" ";
//		}cout<<endl;
//	}cout<<endl;
//	for(int i=1;i<=n;i++){
//		for(int j=1;j<=n;j++){
//			cout<<dpmax[i][j]<<" ";
//		}cout<<endl;
//	}
}
int main() {
	input();
	init();
	Dp();
	output();
	return 0;
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值