• HDU 2829 区间DP & 前缀和优化 & 四边形不等式优化


    HDU 2829 区间DP & 前缀和优化 & 四边形不等式优化

    n个节点n-1条线性边,炸掉M条边也就是分为m+1个区间 问你各个区间的总策略值最少的炸法 就题目本身而言,中规中矩的区间DP问题 d

    p[i][j]表示前i个节点,分为j个区间的最优策略值 cost[i][j]为从i到j节点的策略值 所以dp[i][j] = min(dp[k-1][j-1] + cost[k][i]

    但是复杂度太高了 可以优化的地方有: cost数组值得求取: 考虑到cost(i,j)=ΣAxAy (i≤x<y≤j) 而(Ai+...+Aj)^2=ΣAxAy (i≤x,y≤j) 于是可以得到: cost(i,j)=((Ai+...+Aj)^2-(Ai^2+...+Aj^2))/2 这是一个优化后线性n的等式 式子中的若干连续项的和与若干连续项的平方和 是可以用  前缀和   预先处理的, 所以设sum(i)=A1+...+Ai,sqsum(i)=A1^2+...+Ai^2, 将原式化为: cost(i,j)=((sum(j)-sum(i-1))^2-(sqsum(j)-sqsum(i-1)))/2

    又因为是经典的区间DP问题所以可以用四边形不等式进行优化

    设s[i][j]为dp[i][j]的前导状态dp[i][j] = dp[s[i][j][j-1] + cost[s[i][j]+1][j]之后我们枚举k的时候只要枚举s[i][j-1]<=k<=s[i+1][j],此时j必须从小到大遍历i必须从大到小。

    #include <iostream>
    #include <algorithm>
    #include <string.h>
    #include <cstdio>
    #define inf (1 << 30)
    using namespace std;
    const int maxn = 1e3 + 1e2;
    int dp[maxn][maxn];
    int s[maxn][maxn];
    //设s[i][j]为dp[i][j]的前导状态
    //dp[i][j] = dp[s[i][j][j-1] + cost[s[i][j]+1][j]
    //之后我们枚举k的时候只要枚举
    //s[i][j-1]<=k<=s[i+1][j],此时j必须从小到大遍历
    //i必须从大到小。
    int cost[maxn][maxn];
    int sum[maxn],powsum[maxn];
    int a[maxn];
    /*int get_cost(int l,int r)
    {
        if(r < l)return 0;
        return ((sum[r] - sum[l-1]) * (sum[r] - sum[l-1]) - (powsum[r] - powsum[l-1])) / 2;
    }*/
    void init()
    {
        memset(sum,0,sizeof(sum));
        memset(powsum,0,sizeof(powsum));
        memset(cost,0,sizeof(cost));
    }
    int main()
    {
        int n,m;
        while(~scanf("%d%d",&n,&m))
        {
    
            if(n == m && n == 0)break;
            init();
            //m++;
            //前缀和处理
            for(int i = 1;i <= n;i++)
            {
                scanf("%d",&a[i]);
                sum[i] = sum[i-1] + a[i];
                powsum[i] = powsum[i-1] + a[i] * a[i];
            }
    
            /*for(int i = 1;i <= n;i++)
                for(int j = 1;j <= n;j++)
            {
                if(j < i) cost[i][j] = 0;
                else cost[i][j] = cost[i][j-1] + a[j] * (sum[j-1] - sum[i-1]);
            }*/
            //特殊值预处理
            //这里没有m++但是0代表分一块
            for(int i = 0;i <= n;i++)
            {
                dp[i][0] = cost[1][i];
    //            dp[i][0] = get_cost(1,i);
                s[i][0] = 0;
                s[n+1][i] = n;//外面的界限出界后的特殊处理
            }
            //区间DP & 四边形不等式
            for(int j = 1;j <= m;j++)//分几部分
            {
                for(int i = n;i >= 1;i--)//前n个节点
                {
                    dp[i][j]  = inf;
                    for(int k = s[i][j-1];k <= s[i+1][j];k++)
                    {
                        if(dp[i][j] > dp[k][j-1] + get_cost(k+1,i))
                        {
                            dp[i][j] = dp[k][j-1] + get_cost(k+1,i);
                            s[i][j] = k;//确定上一个状态
                        }
                    }
                }
            }
            printf("%d
    ",dp[n][m]);
        }
        return 0;
    }
    
  • 相关阅读:
    CentOS 7中cAdvisor的安装过程
    怎么查看centos版本
    使docker命令不用加sudo的方法
    Linux进程相关函数system,fork,exec函数族的区别
    git pre-commit hook failed 解决办法
    lint-staged 教程
    用webpack将多个scss文件打包成一个css文件
    js连按键盘事件
    vscode快捷键
    vim 操作命令大全
  • 原文地址:https://www.cnblogs.com/DF-yimeng/p/9355133.html
Copyright © 2020-2023  润新知