区间DP思路和题型总结

区间DP的总结和整理

基本思路

  • 确定状态表示,即表示的状态是什么

  • 确定划分依据,即的含义和取值(是否能取到L或者R)

  • 基本框架(枚举区间长度和左端点,并确定右端点,通过k进行转移)

    for (int len = 2;len <= n;++len){
        for (int i = 1;i + len - 1 <= n;++i){
            int r = i + len - 1;int l = i;
            f[l][r] = INF;
            for (int k = l;k < r;++k)
                f[l][r] = min(f[l][r],f[l][k] + f[k+1][r] + s[r] - s[l-1]);
        }
    }
    cout << f[1][n] << endl;

基本题型

  • 石子合并

  • 环形石子合并

    将总区间扩展成两端(长度为2n),然后直接转化为线性的区间dp

    int a[N],s[N];
    int f[N][N],g[N][N];
    int n;
    int main(){
        cin >> n;
        for (int i = 1;i <= n;++i){
            cin >> a[i];
            a[i+n] = a[i];
        }
        for (int i = 1;i <= n*2;++i){
            s[i] = s[i-1] + a[i];
        }
    
        memset(f,0x3f,sizeof f);
        memset(g,-0x3f,sizeof g);
        for (int len = 1;len <= n * 2;++len){
            for (int i = 1;i + len - 1 <= n * 2;++i){
                int l = i,r =  i + len - 1;
                if (l == r)
                    f[l][r] = g[l][r] = 0;
                for (int k = l;k < r;++k){
                    f[l][r] = min(f[l][r],f[l][k] + f[k+1][r] + s[r] - s[l-1]);
                    g[l][r] = max(g[l][r],g[l][k] + g[k+1][r] + s[r] - s[l-1]);
                }
            }
        }
    
        int maxn = -INF,minn = INF;
        for (int i = 1;i <= n;++i){
            maxn = max(maxn,g[i][i+n-1]);
            minn = min(minn,f[i][i+n-1]);
        }
        cout << minn << endl << maxn << endl;
        return 0;
    }
  • 能量项链(同上),注意k的取值和最后答案的选取

  • 加分二叉树,这题比较特别,​选取的是数上所有的根节点,f[l][k-1]为左子树的得分,f[k+1][r]为右子树的得分,每次转移计算根节点的得分。

    void dfs(int l,int r){
        if (l > r) return;
        int root = g[l][r];
        cout << root << " ";
        dfs(l,root-1);
        dfs(root + 1,r);
    }
    
    int main(){
        cin >> n;
        for (int i = 1;i <= n;++i)   cin >> a[i];
    
        for (int len = 1;len <= n;++len){
            for (int l = 1;l + len - 1 <= n;++l){
                int r = l + len - 1;
                if (l == r){
                    g[l][r] = l;
                    f[l][r] = a[l];
                }
                else{
                    for (int k = l;k <= r;++k){
                        int left = k == l ? 1 : f[l][k-1];
                        int right = k == r ? 1 : f[k+1][r];
                        int s = left * right + a[k];
                        if (f[l][r] < s){
                            f[l][r] = s;
                            g[l][r] = k;
                        }
                    }
                }
            }
        }
        cout << f[1][n] << endl;
        dfs(1,n);
        puts("");
        return 0;
    }
  • 凸多边形的划分(与能量项链类似,注意数据范围)

    inline __int128 read(){
        __int128 x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){
            if(ch == '-')
                f = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9'){
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    inline void print(__int128 x){
        if(x < 0){
            putchar('-');
            x = -x;
        }
        if(x > 9)
            print(x / 10);
        putchar(x % 10 + '0');
    }
    
    int n;
    __int128 f[N][N];
    __int128 a[N];
    int main(){
        cin >> n;
        for (int i = 1;i <= n;++i)   a[i] = read();
    
        for (int len = 3;len <= n;++len){
            for (int l = 1;l + len - 1 <= n;++l){
                int r = l + len - 1;
                f[l][r] = INF;
                for (int k = l + 1;k < r;++k){
                    //if (a[l] * a[k] * a[r] < 0)    puts("yes");
                    f[l][r] = min(f[l][r],f[l][k] + f[k][r] + (a[l] * a[k] * a[r]));
                }
            }
        }
        print(f[1][n]);
        puts("");
        return 0;
    }
  • 棋盘划分,二位的区间dp,区间的定义变为了矩形而不是线性的,参照二位前缀和的定义

    int n,m;
    double f[M][M][M][M][N];
    int s[N][N];
    double X;
    
    double get(int x1,int y1,int x2,int y2){
        double res = s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1] - X;
        return res * res /  n;
    }
    
    double dp(int x1,int y1,int x2,int y2,int k){
        double &v = f[x1][x2][y1][y2][k];
        if (v >= 0)  return v;
        if (k == 1) return v = get(x1,y1,x2,y2);
    
        v = INF;
        for (int i = x1;i < x2;++i){
            v = min(v,dp(x1,y1,i,y2,k-1) + get(i+1,y1,x2,y2));
            v = min(v,dp(i+1,y1,x2,y2,k-1) + get(x1,y1,i,y2));
        }
    
        for (int j = y1;j < y2;++j){
            v = min(v,dp(x1,y1,x2,j,k-1) + get(x1,j + 1,x2,y2));
            v = min(v,dp(x1,j + 1,x2,y2,k-1) + get(x1,y1,x2,j));
        }
        return v;
    }
    
    int main(){
        cin >> n;
        for (int i = 1;i <= 8;++i){
            for (int j = 1;j <= 8;++j){
                cin >> s[i][j];
                s[i][j] += s[i-1][j] + s[i][j-1] - s[i-1][j-1];
            }
        }
        X = (double) s[8][8] / n;
        memset(f,-1,sizeof f);
        printf("%.3lf\n",sqrt(dp(1,1,8,8,n)));
        return 0;
    }
全部评论

相关推荐

自从我室友在计算机导论课上听说了“刷&nbsp;LeetCode&nbsp;是进入大厂的敲门砖”,整个人就跟走火入魔了一样。他在宿舍门口贴了一张A4纸,上面写着:“正在&nbsp;DP,请勿打扰,否则&nbsp;Time&nbsp;Limit&nbsp;Exceeded。”日记本的扉页被他用黑色水笔加粗描了三遍:“Talk&nbsp;is&nbsp;cheap.&nbsp;Show&nbsp;me&nbsp;the&nbsp;code。”连宿舍聚餐,他都要给我们讲解:“今天的座位安排可以用回溯算法解决,但为了避免栈溢出,我建议用动态规划。来,这是状态转移方程:dp[i][j]&nbsp;代表第&nbsp;i&nbsp;个人坐在第&nbsp;j&nbsp;个位置的最优解。”我让他去楼下取个快递,他不直接去,非要在门口踱步,嘴里念念有词:“这是一个图的遍历问题。从宿舍楼(root)到驿站(target&nbsp;node),我应该用&nbsp;BFS&nbsp;还是&nbsp;DFS?嗯,求最短路径,还是广度优先好。”和同学约好出去开黑,他会提前发消息:“集合点&nbsp;(x,&nbsp;y),我们俩的路径有&nbsp;k&nbsp;个交点,为了最小化时间复杂度,应该在&nbsp;(x/2,&nbsp;y/2)&nbsp;处汇合。”有一次另一个室友低血糖犯了,让他帮忙找颗糖,他居然冷静地分析道:“别急,这是一个查找问题。零食箱是无序数组,暴力查找是&nbsp;O(n)。如果按甜度排序,我就可以用二分查找,时间复杂度降到&nbsp;O(log&nbsp;n)。”他做卫生也要讲究算法效率:“拖地是典型的岛屿问题,要先把连通的污渍区块都清理掉。倒垃圾可以用双指针法,一个指针从左往右,一个从右往左,能最快匹配垃圾分类。”现在我们宿舍的画风已经完全变了,大家不聊游戏和妹子,对话都是这样的:“你&nbsp;Two&nbsp;Sum&nbsp;刷了几遍了?”“别提了,昨天遇到一道&nbsp;Hard&nbsp;题,我连暴力解都想不出来,最后只能看题解。你呢?”“我动态规划还不行,总是找不到最优子结构。今天那道接雨水给我整麻了。”……LeetCode&nbsp;真的害了我室友!!!
老六f:编程嘉豪来了
AI时代还有必要刷lee...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务