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\n",dp[n][m]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Df_cjc/article/details/81253662