区间dp(区间动态规划)

区间dp

区间dp

一、基本思路:分治思想,将一个序列问题不断往下分,分成满足问题条件的最小区间(长度不一定为1《其实是基本不为1》,2、3比较常见),然后解决问题。

二、dp细节:枚举i,j,k,(i是区间长度,j是起点,k不一定有)

三、基本代码框架:

(1)dfs思路:

   dp[][]存需要使用的数值,要初始化成memset(dp,-1sizeof(dp));

   a[]存输入值

   int dfs(int st, int ed)//输入区间两端点

   {
     if(st + a >= ed) return 0;

     if(dp[st][ed] != -1 ) return dp[st][ed];

       dp[st][ed] = 1e9;

       for(int i = 1;i <= n;i++)

       {
       dp[st][ed] = (min/max){dp[st][ed], dp[st][i] + dp[i][ed] + 实际情况需要的计算(常用a[]};

     }

   }

主函数:dfs(1,n); 结果:dp[1][n];

注意:在dp状态转移方程部分,dp[i][ed] 中i可以按照实际问题要求改成i+1(通常是一个区间端点左右的数还需在处理该区间内部数据时用到)

(2)dp做法

  int maxa = 1e9;

  dp[][]存答案

  for(int i = 2;i <= n - 1;i++)//i表示长度,j表示起点,k表示当前区间内最后删除的一个点 (闭区间)
  {

    for(int j = 1;i + j <= n;j++)
    {

      dp[j][i + j] = maxa;
      for(int k = j + 1;k < i + j;k++)
      {

       dp[j][i + j] = min(dp[j][i+j],dp[j][k] + dp[k][i + j] + a[j] * a[k] * a[i + j]);//此时已经处理完了该区间内的所有的数据,故可看作只有最后一个删掉的数,和区间两端的数参与最后一次运算 

      }//把两个区间拼起来 (k是分界点)
    }

  }

四、例题(持续补充)

(1)乘法游戏

输入n个数,每次删去一个数,则得分 = 删去的数 * 前一个数 * 后一个数,头尾的数不可删去,要求将所有数都删去,求得分最小值

样例:

输入: 5

3 2 1 7 9

输出: 96

思路:我们很容易想到如果想求整体数列的最小值,就一定要让每一个小部分的值最小(如果连这个小部分的值都可以更小,那么整体值一定可以更小)。

所以我们可以先把整个数列分成很多个小区间,然后求每个区间所能达到的最优情况,再把区间合起来,最终得到的值一定是最小的。

(2)涂色(洛谷p4170)

假设你有一条长度为 55 的木版,初始时没有涂过任何颜色。你希望把它的 55 个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 55 的字符串表示这个目标:RGBGR。

每次你可以把一段连续的木版涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木版涂成 RRRRR,第二次涂成 RGGGR,第三次涂成 RGBGR,达到目标。

用尽量少的涂色次数达到目标。

样例:AAAAA   输出:1

样例:RGBGR  输出:3

思路:我们可以用一种神奇的思路解决问题,就是最开始一个位置操作一次,则ans = n,然后再判断哪些位置可以一次涂完。

可以发现在一个小区间内,如aba,因为两侧都有相同字符,即区间端点可以一次解决,则ans = (1 + 1 + 1) - 1;

所以,每次发现区间的端点相等,则把该区间的ans减一;

这种情况当然把整个序列分成无数个小序列(最短序列每个长度为1),再进行上面的操作,就能得出正确答案。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值