(自己的理解:首先考虑单调队列,不行时考虑斜率,再不行就考虑不等式什么的东西)

 

 

当dp的状态转移方程dp[i]的状态i需要从前面(0~i-1)个状态找出最优子决策做转移时 我们常常需要双重循环

(一重循环跑状态 i,一重循环跑 i 的所有子状态)这样的时间复杂度是O(N^2)而 斜率优化或者四边形不等式优化后的DP

可以将时间复杂度缩减到O(N)

O(N^2)可以优化到O(N) ,O(N^3)可以优化到O(N^2),依次类推

斜率优化DP和四边形不等式优化DP主要的原理就是利用斜率或者四边形不等式等数学方法

在所有要判断的子状态中迅速做出判断,所以这里的优化其实是省去了枚举i的子状态的循环,几乎就是直接把最优的子状态找出来了

其中四边形不等式优化是用数组s边跑边求最优的子状态,例如用s[i][j]保存dp[i][j]的最优子状态

斜率优化的话是将后面可能用到的子状态放到队列中,要求的当前状态的最优状态就是队首元素q[head]

另外,网上见到很多用二分+DP解斜率优化的问题。

以dp求最小值为例:

主要的解题步骤就是先写出dp的状态转移方程,然后选取两个子状态p,q

假设p < q而决策q比p更好,求出斜率不等式,然后就可以写了

至于经常有题目控制子决策的范围什么的(比如控制区间长度,或者控制分组的组数),就需要具体情况具体分析

1  HDU 1300 Pearls

 

最最最简单的斜率DP优化的题,就算不用优化,O(N^2)的算法也可以AC

这题绝壁是最最最适合入门的斜率DP的题,我发誓!!!

版本一:(O(N^2))

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s) 
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
/*
    dp[i]表示买前i种珍珠的最少花费
    
    dp[i] = min(dp[j] + (sum[i] - sum[j] + 10)*w[i])
    其中sum[i]-sum[j]表示第j+1种珍珠到第i种珍珠所需的数量
    w[i]表示第i种珍珠的价值
     
*/
const int N = 111;
int w[N],dp[N],sum[N];
int main()
{
    int T;Sint(T);
    while (T--)
    {
        int n;Sint(n);
        for (int i = 1,x;i <= n;++i)
        {
            Sint2(x,w[i]);
            sum[i] = sum[i-1] + x;
        }
        dp[1] = (sum[1]+10)*(w[1]);
        for (int i = 2;i <= n;++i)
        {
            dp[i] = dp[i-1] + (sum[i]-sum[i-1]+10)*w[i];
            for (int j = 0;j < i-1;++j)
            {
                dp[i] = min(dp[i],dp[j] + (sum[i]-sum[j]+10)*w[i]);
            }
        }
        Pintc(dp[n],'\n');
    }
    return 0;
}
View Code

相关文章: