ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

区间DP

2021-12-07 13:34:05  阅读:172  来源: 互联网

标签:dpp int 合并 DP 区间 最优 dp


概念

区间类型动态规划是线性动态规划的拓展,它在分阶段划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来有很大的关系。(例:f[i][j]=f[i][k]+f[k+1][j])

区间类动态规划的特点:

  • 合并:即将两个或多个部分进行整合。
  • 特征:能将问题分解成为两两合并的形式。
  • 求解:对整个问题设最优值,枚举合并点,将问题分解成为左右两个部分,最后将左右两个部分的最优值进行合并得到原问题的最优值。

 

石子合并 [P1880]

题目描述

在一个圆形操场的四周摆放 N 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出一个算法,计算出将 N 堆石子合并成 1 堆的最小得分和最大得分。

 

思路:

首先这是一个环形结构,我们需要把它转换为线性结构来处理会比较方便,所以我们需要把线性长度增加一倍,使得首位可以相连。

for(int i=1;i<=n;i++)
scanf("%d", &a[i]),a[n+i]=a[i],s[i]=s[i-1]+a[i];

 这样我们就可以在线性结构上解决问题,这题是一道区间DP的模板题。

对应到动态规划中,就是两个长度较小的区间上的信息向一个更长的区间发生了转移,划分点k就是转移的决策,区间长度就是DP的阶段。根据动态规划“选择最小的能覆盖状态空间的维度集合”的思想,可以只用左、右端点表示DP的状态。

 

dp[i][j]表示区间从i到j的最小合并值;

dpp[i][j]表示区间从i到j的最大合并值;

s[i]表示前缀和;

通过枚举断点k来找出每个区间最优值再合并;

状态转移方程:

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+s[j]-s[i-1]);
dpp[i][j]=max(dpp[i][j],dpp[i][k]+dpp[k+1][j]+s[j]-s[i-1]);

 

完整代码:

#include<bits/stdc++.h>
using namespace std;
int n,a[3005],s[3005],dp[3005][3005],dpp[3005][3005];

int main()
{
    scanf("%d", &n);
    for(int i=1;i<=n;i++)//断环成链 
    scanf("%d", &a[i]),a[n+i]=a[i],s[i]=s[i-1]+a[i];
    for(int i=n+1;i<=2*n;i++)//前缀和 
    s[i]=s[i-1]+a[i];
    for(int l=2;l<=n;l++)//区间长度 
    {
        for(int i=1;l+i-1<=2*n;i++)//左端点 
        {
            int j=l+i-1;//右端点 
            dp[i][j]=1e8;
            dpp[i][j]=-1e8;
            for(int k=i;k<j;k++)//断点k 
            {
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+s[j]-s[i-1]);
                dpp[i][j]=max(dpp[i][j],dpp[i][k]+dpp[k+1][j]+s[j]-s[i-1]);
            }
        }
    }
    int maxx=-1e8,minn=1e8;
    for(int i=1;i<=n;i++)//遍历找最大最小值 
    {
    	maxx=max(maxx,dpp[i][i+n-1]);
    	minn=min(minn,dp[i][i+n-1]);
	}
	printf("%d\n%d",minn,maxx);
    return 0;
}

 

能量项链 [P1063]

#include<bits/stdc++.h>
using namespace std;
int n,maxx,a[300],dp[300][300];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i],a[i+n]=a[i];
	}
	for(int i=2;i<2*n;i++)
	{
		for(int j=i-1;j>=1&&i-j<n;j--) 
		{
			for(int k=j;k<i;k++)
			{
		    //转移方程 = max(原来能量,左边能量 +右边能量 +将两区间合并的能量);
		    //两区间合并能量=左区间第一个珠子*右区间第一个珠子*总区间后面一个珠子; 
				dp[j][i]=max(dp[j][i],dp[j][k]+dp[k+1][i]+a[i+1]*a[k+1]*a[j]);
				maxx=max(dp[j][i],maxx);
			}
		}
	}
	cout<<maxx;
	return 0;
}

 

 

总结:

基本特征:将问题分解成为两两合并的形式。

解决方法:对整个问题设最优值,枚举合并点,将问题分解成为左右两个部分,再将左右两个部分的最优值进行合并得到原问题的最优值。

 

设i到j的最优值,枚举剖分(合并)点,将(i,j)分成左右两区间,分别求左右两边最优值,如下图:

 

状态转移方程的一般形式如下:

 

 

 

 

 

标签:dpp,int,合并,DP,区间,最优,dp
来源: https://www.cnblogs.com/wxk1213/p/15655070.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有