首页 > 其他分享 >区间DP小结(附经典例题) 转载

区间DP小结(附经典例题) 转载

时间:2023-04-27 18:02:31浏览次数:51  
标签:include const int sum maxn DP 例题 小结 dp

区间DP

转载自:原博客

一、定义

​ 区间DP是线性动态规划的扩展,适用场景为每段区间的最优解可以通过更小区间的最优解得到。所以我们一般的解题思路都是先在小区间得到最优解,然后总结出递推公式,利用小区间的最优解求大区间的最优解。

二、实现伪代码

//mst(dp,0) 初始化dp数组
for(int i=1;i<=n;i++)
{
    dp[i][i]=初始值
}
for(int len=2;len<=n;len++)  //枚举区间长度
for(int i=1;i<=n;i++)        //枚举起点
{
    int j=i+len-1;           //区间终点
    if(j>n) break;           //越界结束
    for(int k=i;k<j;k++)     //枚举分割点,构造状态转移方程
    {
        dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);
    }
}

三、经典例题
1. 石子合并问题
1.1 石子合并(一)
石子合并(一)
Time Limit: 1000 MS Memory Limit: 65535 K
Problem Description

有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。

Input
​有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0<n<200)个数,分别表示这n堆石子的数目,用空格隔开
Output

输出总代价的最小值,占单独的一行

Examples

Input

3

1 2 3

7

13 7 8 16 21 4 18

Output

9

239

【题目链接】link

【思路】

我们dp[i][j]来表示合并第i堆到第j堆石子的最小代价。

那么状态转移方程为

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);

其中w[i][j]表示把(i,j)和(k+1,j)这两部分合并起来的代价,即从第i堆到第j堆石子个数的和,为了方便查询并提高效率,我们可以利用前缀和预处理,用sum[i]表示从第1堆到第i堆的石子个数和,那么w[i][j]=sum[j]-sum[i-1]。

#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)
 
typedef long long ll;
const int maxn = 205;
const ll mod = 1e9+7;
const ll INF = 1e18;
const double eps = 1e-9;
 
int n,x;
int sum[maxn];
int dp[maxn][maxn];
 
int main()
{
    while(~scanf("%d",&n))
    {
        sum[0]=0;
        mst(dp,0x3f);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            sum[i]=sum[i-1]+x;
            dp[i][i]=0;
        }
        for(int len=2;len<=n;len++)
        for(int i=1;i<=n;i++)
        {
            int j=i+len-1;
            if(j>n) continue;
            for(int k=i;k<j;k++)
            {
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
            }
        }
        printf("%d\n",dp[1][n]);
    }
    return 0;
}

【拓展】平行四边形优化

上面的代码运行时间在240ms左右,通过这题完全没问题,但我们还可以考虑优化。

由于状态转移时是三重循环的,我们想能否把其中一层优化呢?尤其是枚举分割点的那个,显然我们用了大量的时间去寻找这个最优分割点,所以我们考虑把这个点找到后保存下来

用s[i][j]表示区间[i,j]中的最优分割点,那么第三重循环可以从[i,j-1)优化到【s[i][j-1],s[i+1][j]】。(这个时候小区间s[i][j-1]和s[i+1][j]的值已经求出来了,然后通过这个循环又可以得到s[i][j]的值)。

关于平行四边形优化的证明可以参考这篇博客: link

// 32ms
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)
 
typedef long long ll;
const int maxn = 205;
const ll mod = 1e9+7;
const ll INF = 1e18;
const double eps = 1e-9;
 
int n,x;
int sum[maxn];
int dp[maxn][maxn];
int s[maxn][maxn];
 
int main()
{
    while(~scanf("%d",&n))
    {
        sum[0]=0;
        mst(dp,0x3f);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            sum[i]=sum[i-1]+x;
            dp[i][i]=0;
            s[i][i]=i;
        }
        for(int len=2;len<=n;len++)
        for(int i=1;i<=n;i++)
        {
            int j=i+len-1;
            if(j>n) continue;
            for(int k=s[i][j-1];k<=s[i+1][j];k++)
            {
                if(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]<dp[i][j])
                {
                    dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
                    s[i][j]=k;
                }
            }
        }
        printf("%d\n",dp[1][n]);
    }
    return 0;
}

1.2 Monkey Party
Monkey Party
Time Limit: 2000 MS Memory Limit: 65535 K
Problem Description
Far away from our world, there is a banana forest. And many lovely monkeys livethere. One day, SDH(Song Da Hou), who is the king of banana forest, decides tohold a big party to celebrate Crazy Bananas Day. But the little monkeys don’tknow each other, so as the king, SDH must do something.
Now there are n monkeys sitting in a circle, and each monkey has a makingfriends time. Also, each monkey has two neighbor. SDH wants to introduce themto each other, and the rules are:
1.every time, he can only introduce one monkey and one of this monkey’sneighbor.
2.if he introduce A and B, then every monkey A already knows will know everymonkey B already knows, and the total time for this introducing is the sum ofthe making friends time of all the monkeys A and B already knows;
3.each little monkey knows himself;
In order to begin the party and eat bananas as soon as possible, SDH want toknow the mininal time he needs on introducing.

Input

There is several test cases. In each case, the first line is n(1 ≤ n ≤ 1000), whichis the number of monkeys. The next line contains n positive integers(less than1000), means the making friends time(in order, the first one and the last oneare neighbors). The input is end of file.

Output

For each case, you should print a line giving the mininal time SDH needs on introducing.

Examples

Input

8

5 2 4 7 6 1 3 9

Output

105

【题目链接】link

【题意】

问题转化后其实就是环形石子合并,即现在有围成一圈的若干堆石子,其他条件跟前面那题相同,问合并所需最小代价——上一题的升级版

【思路】

我们需要做的是尽量向简单的问题转化,可以把前n-1堆石子一个个移到第n个后面,那样环就变成了线,即现在有2*n-1堆石子需要合并,我们只要求下面的式子即可。求法与上面那题完全一样。

min(dp[s][s+n-1]),s∈ \in∈[1,n]
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)
 
typedef long long ll;
const int maxn = 1005*2;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
 
int a[maxn];
int sum[maxn];
int dp[maxn][maxn];
int s[maxn][maxn];
 
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        sum[0]=0;
        mst(dp,0x3f);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
            s[i][i]=i;
            dp[i][i]=0;
        }
        for(int i=1;i<n;i++)
        {
            sum[i+n]=sum[i+n-1]+a[i];
            s[i+n][i+n]=i+n;
            dp[i+n][i+n]=0;
        }
        for(int len=2;len<=n;len++)
        for(int i=1;i<=2*n-1;i++)
        {
            int j=i+len-1;
            if(j>2*n-1) break;
            for(int k=s[i][j-1];k<=s[i+1][j];k++)
            {
                if(dp[i][j]>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
                {
                    dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
                    s[i][j]=k;
                }
            }
        }
        int ans=INF;
        for(int i=1;i<=n;i++)
        {
            ans=min(ans,dp[i][i+n-1]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

标签:include,const,int,sum,maxn,DP,例题,小结,dp
From: https://www.cnblogs.com/jyssh/p/17359823.html

相关文章

  • P4316 绿豆蛙的归宿(期望dp)
    题目描述给出张n个点m条边的有向无环图,起点为11,终点为n,每条边都有一个长度,并且从起点出发能够到达所有的点,所有的点也都能够到达终点。绿豆蛙从起点出发,走向终点。到达每一个顶点时,如果该节点有k条出边,绿豆蛙可以选择任意一条边离开该点,并且走向每条边的概率为1/k。现......
  • [C++11]左值、右值、左值引用、右值引用小结
     左值和右值左值:指表达式结束后依然存在的持久对象,可以取地址,具名变量或对象右值:表达式结束后就不再存在的临时对象,不可以取地址,没有名字。比如inta=b+c;,a就是一个左值,可以对a取地址,而b+c就是一个右值,对表达式b+c取地址会报错。C++11中右值又由两个概念组成:将亡值和纯......
  • 设计模式小结
    简单工厂模式将具有相同属性事物用一个抽象基类,里面具有抽象方法来作为父类,然后其他子类通过继承来实现这个基类,通过重写实现基类里面的抽象方法创建一个工厂方法,通过父类变量来策略模式就是在简单工厂模式的基础上,将工厂方法改成策略对象,然后去调用该对象的重写基类的抽象方法单一......
  • [生活日记]参与unity非游戏行业开发者大会小结
    今天下午花了半天时间公司全体都去人民广场参与了一个unity非游戏行业开发者大会,主要了解到unity这款全球顶尖之一的游戏引擎的一个发展史,从05年三个美国人技术研发开始,一直到12年开始引进中国,经过这短短两年左右的时间,获得了逛到游戏开发者的喜爱和肯定,它始于游戏,但非终止于游戏,今......
  • CF960F Pathwalks | 线段树优化DP
    题目设\(dp[x,w]\)为以结点\(x\)为结尾,且最后一条边边权为\(w\)的最长路径长度。考虑根据顺序加边,对于边\((u,v)\),更新\[dp[v,w]=\max_{w'<w}\{dp[u,w']\}+1\]对于每个节点,建一棵线段树,维护\(dp[x]\),这样每次更新\(dp[v,w]\)就相当于在\(dp[u]\)所对应的线段树中查询\([......
  • 第四章部分例题(1)
    例4-1题目描述:时钟类的完整程序代码实现:#include<iostream>usingnamespacestd;classClock{private:inthour,minute,second;public:voidsetTime(intnewH=0,intnewM=0,intnewS=0){hour=newH;minute=newM;......
  • DP 概论
    对于一个题目,我们有暴力搜索算法。而dp就是尽可能将同一类的东西合并到一个状态上。如何检查dp的正确性?检查集合内部转移是否一致。检查方案数是否正确。状压dp有好几种状态压缩的方式:记录“选了哪些东西”这样的信息,可以用一个长度为\(n\)的01串,对应一个十进制......
  • [每天例题]蓝桥杯 C语言 顺子日期
    顺子日期题目https://www.lanqiao.cn/problems/2096/learning/?page=3&first_category_id=1&sort=students_count&difficulty=30 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。小明特别喜欢顺子。顺子指的就是连续的三个数字:123、456等。顺子日期......
  • 【DP】LeetCode 740. 删除并获得点数
    题目链接740.删除并获得点数思路分析动态规划题目的时候只需要考虑最后一个阶段,因为所有的阶段转化都是相同的,考虑最后一个阶段容易发现规律在数组的动态规划问题中,一般dp[i]都是表示以nums以前i个元素组成(即nums[i-1])的状态;dp[i][j]分别表示以nums1前i个元素(......
  • C# 获取系统DPI缩放比例以及分辨率大小
    一般方法System.Windows.Forms.Screen类//获取当前主屏幕分辨率intscreenWidth=Screen.PrimaryScreen.Bounds.Width;intscreenHeight=Screen.PrimaryScreen.Bounds.Height;//获取指定屏幕分辨率ScreensecondaryScreen=Screen.AllScreens[1];intsecondaryScree......