区间DP

1. 区间DP概述
  • 概念:区间dp就是在区间上进行动态规划,求解一段区间上的最优解。主要是通过合并小区间的 最优解进而得出整个大区间上最优解的dp算法。

  • 核心思路:既然让我求解在一个区间上的最优解,那么我把这个区间分割成一个个小区间,求解每个小区间的最优解,再合并小区间得到大区间即可。

  • 两类模板

    • 常规区间DP模板:

      for(int len=1;len<n;len++){
          for(int l=1;l+len-1<=n;l++){
              int r=l+len-1;
              for(int k=l;k<r;k++){
                  dp[l][r]=min(d[[l][r],dp[l][k]+d[k+1][r]]+temp);
              }
          }
      }
      
    • 记忆化搜索模板:

      int dfs(int x,int y){
          //出口
          for(...){
              dp[..][..]=min(dp[..][..],dfs(..,..));
          }
      }
      
2.几种的区间DP:
  • 朴素版本:给你一条串,求某一段长度固定的区间和最大值。

    • 例题:石子和并

    • //关键代码
      for(int len=2;len<=n;len++){
          for(int l=1;l+len-1<=n;l++){
              int r=l+len-1;
              dp[l][r]=1e9;
              for(int k=l;k<r;k++){
                  dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+b[r]-b[l-1]);
                  //b数组为前缀和
              }
          }
      }
      
  • 环形:在下标n与0之间断开环,同时在下标n的后面再添加一段下标0到n的元素。

    • 例题:环形石子合并

    • //关键代码
      for(int len=1;len<=n;len++){
          for(int l=1;l+len-1<=2*n;l++){
              int r=l+len-1;
              if(l==r)
                  dp1[l][r]=0;
              for(int k=l;k<r;k++){
                  dp1[l][r]=min(dp1[l][r],dp1[l][k]+dp1[k+1][r]+b[r]-b[l-1]);
              }
          }
      }
      
  • 二维区间DP

    • 例题:棋盘分隔

    • 题目大意:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割。使最后剩下的矩形棋盘共有n块矩形棋盘。并让各矩形棋盘总分的均方差最小。

    • 分析:

      • 首先确定dp维数:我们可以通过两个坐标(两对(x,y))来确定矩阵大小,再用一个变量来表示可以划分几次。
        • 即:dp[x1][y1][x2][y2][x]:矩阵左上角顶点为(x1,y1),右下角顶点为(x2,y2)的矩阵,且分成x块后,总均方差最小的值。
      • 由于维数过多,如采用常规区间DP模板,书写较为复杂。所以这里使用记忆化搜索更为方便。
      • 因为需要求面积和,所以可以首先使用二位前缀和预处理。
      • 再使用记忆化搜索进行DFS
        • 首先判断出口条件。
        • 之后由于是二维区间DP,所以它存在两种切法:横切,竖切,所以我们都需要进行考虑。
        • DP转移方程为:(get为求二维前缀和)
          • 横切:dp[x1][y1][x2][y2][k]=get(x1,y1,i,y2)+dfs(i+1,y1,x2,y2,k-1)
            dp[x1][y1][x2][y2][k]=get(i+1,y1,x2,y2)+dfs(x1,y1,i,y2,k-1)
          • 竖切:dp[x1][y1][x2][y2][k]=get(x1,y1,x2,i)+dfs(x1,i+1,x2,y2,k-1)
            dp[x1][y1][x2][y2][k]=get(x1,i+1,x2,y2)+dfs(x1,y1,x2,i,k-1)
    • //关键代码
      double dfs(int x1,int y1,int x2,int y2,int k){
          double &v=dp[x1][y1][x2][y2][k];
          if (v>=0)   return v;
          if(k==1)    return get(x1,y1,x2,y2);
          v=INF;
          for(int i=x1;i<x2;i++){
              v=min(v,get(x1,y1,i,y2)+dfs(i+1,y1,x2,y2,k-1));
              v=min(v,get(i+1,y1,x2,y2)+dfs(x1,y1,i,y2,k-1));
          }
          for(int i=y1;i<y2;i++){
              v=min(v,get(x1,y1,x2,i)+dfs(x1,i+1,x2,y2,k-1));
              v=min(v,get(x1,i+1,x2,y2)+dfs(x1,y1,x2,i,k-1));
          }
          return v;
      }
      
posted @ 2021-03-16 16:05  z_thorn  阅读(43)  评论(0编辑  收藏  举报