【转】斜率优化DP和四边形不等式优化DP整理

当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;
}

当做出暴力DP版本之后,只需再多考虑一步就可以变成斜率优化DP

对于状态转移方程dp[i] = dp[j] + (sum[i]-sum[j]+10)*w[i]

考虑 k < j < i 且假设 i状态由j状态转移得到比由k状态转移得到更优

即:dp[j] + (sum[i]-sum[j]+10)*w[i] <= dp[k] + (sum[i] - sum[k] + 10)*w[i]

(这里取小于号是因为dp保存的是最小花费,花费越小越好,取等是因为j比k大,所以就算k,j一样优也选j)

这个不等式化简之后就是

dp[j] - dp[k] <= w[i]*(sum[j]-sum[k])

这里的w[i]满足单调递增

有了上面的不等式和单调条件就可以斜率优化了,主要做法就是利用单调队列维护满足的点

比如j状态优于k状态,就可以将k永远的剔除了

具体对于子状态的维护见代码里面有2个对队列进行的删除的操作,一个是在求dp[i]时在队首删除

一个是在将状态i加入队列时在队尾删除的操作

版本二:(O(N))

#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种珍珠的价值

    dp[j] - dp[k] <= w[i]*(sum[j]-sum[k])
    The qualities of the classes (and so the prices) are given in ascending order.
    So w[i]单增 --斜率DP
*/
const int N = 111;
int w[N],dp[N],sum[N];
int q[N];
int DP(int i,int j)
{
    return dp[j] + (sum[i]-sum[j]+10)*w[i];
}
int dy(int i,int j)
{
    return dp[i]-dp[j];
}
int dx(int i,int j)
{
    return sum[i]-sum[j];
}
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;
    }
        int head = 0,tail = 0;
        q[tail++] = 0;
        for (int i = 1;i <= n;++i)
        {
            while (head+1<tail&&dy(q[head+1],q[head])<=w[i]*dx(q[head+1],q[head])) ++head;
            dp[i] = DP(i,q[head]);
            while (head+1<tail&&dy(i,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(q[tail-1],q[tail-2])*dx(i,q[tail-1])) --tail;
            q[tail++] = i;
        }
        Pintc(dp[n],‘\n‘);
    }
    return 0;
}

POJ 1260 Pearls   和上面一题一样的

3.HDU 3507 Print Article

列状态转移方程 然后假设 k < j < i 且j决策更好 不等式列出来就好了

#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>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#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;
const int N = 500007;
int dp[N];
int sum[N];
int q[N];
int n,m;
int EX(int x)
{
    return x*x;
}
int getDP(int i,int j)
{
    return dp[j] + m + EX(sum[i]-sum[j]);
}
int getUP(int j,int k)//yj - yk
{
    return (dp[j] + EX(sum[j])) - (dp[k] + EX(sum[k]));
}
int getDown(int j,int k)//xj - xk
{
    return 2*(sum[j] - sum[k]);
}
int main()
{

    while (Sint2(n,m) == 2)
    {
        for (int i = 1,x;i <= n;++i)
        {
            Sint(x);
            sum[i] = sum[i-1] + x;
        }
        int head = 0,tail = 0;
        q[tail++] = 0;//单调队列 (单增)
        for (int i = 1;i <= n;++i)
        {
            //                  getup/getdown  <= sum[i]
            while (head+1<tail&&getUP(q[head+1],q[head])<=sum[i]*getDown(q[head+1],q[head])) head++;
            dp[i] = getDP(i,q[head]);
            //          getup(i,q[tail-1])/getdown(i,q[tail-1]) <= getup(q[tail-1],q[tail-2])/getdown(q[tail-1],q[tail-2])
            while (head+1<tail&&getUP(i,q[tail-1])*getDown(q[tail-1],q[tail-2])<=getUP(q[tail-1],q[tail-2])*getDown(i,q[tail-1]))tail--;
            q[tail++] = i;
        }
        Pintc(dp[n],‘\n‘);
    }
    return 0;
}

4.HDU 2829 Lawrence

#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 Sint2(x,y) scanf("%d %d",&x,&y)
#define Schars(s) scanf("%s",s)
#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][j]表示前j个数分成i组的最小价值
    sum[i]表示前i个数的和
    cost[i]表示前i个数的花费
*/
const int N = 1004;
int sum[N],cost[N],dp[N][N];
int q[N],head,tail;
int n,m;
int EX(int x)
{
    return x*x;
}
int dy(int x,int j,int i)
{
    return dp[x][i] - cost[i] + EX(sum[i]) - (dp[x][j] - cost[j] + EX(sum[j]));
}
int dx(int j,int i)
{
    return sum[i] - sum[j];
}
int DP(int x,int j,int i)
{
    return dp[x][i]+cost[j] - cost[i] - sum[i]*(sum[j]-sum[i]);
}
int main()
{
    while (Sint2(n,m)==2&&(n||m))
    {
        ++m;
        for (int i = 1,x;i <= n;++i)
        {
            Sint(x);
            sum[i] = sum[i-1] + x;
            cost[i] = cost[i-1] + sum[i-1]*x;
        }
        for (int i = 1;i <= n;++i) dp[1][i] = cost[i];
        for(int i = 2;i <= m;++i)
        {
            head = tail = 0;
            q[tail++] = i-1;
            for (int j = i;j <= n;++j)
            {
                while (head+1<tail&&dy(i-1,q[head],q[head+1])<=sum[j]*dx(q[head],q[head+1])) head++;
                dp[i][j] = DP(i-1,j,q[head]);
                while (head+1<tail&&dx(q[tail-2],q[tail-1])*dy(i-1,q[tail-1],j)<=dy(i-1,q[tail-2],q[tail-1])*dx(q[tail-1],j)) --tail;
                q[tail++] = j;
            }
        }
        Pintc(dp[m][n],‘\n‘);
    }
    return 0;
}

5.UVALive 5097 - Cross the Wall

#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("%lld",&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("%lld %lld",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%lld%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
/*
    dp[i][j]表示 前i个人挖j个洞的最小花费
    1.当w[i] <= w[j] && h[i]<=h[j]时 舍弃 (w[i],h[i])
    2.将人按w递增 h递减 排序,即满足 w[j] < w[i]&&h[j] > h[i]

    故dp[i][j] = dp[k][j-1] + w[i]*h[k+1]

*/
const int N = 500007;

struct Node
{
    ll h,w;
}b[N];
int q[N],head,tail;
bool cmp(Node a,Node b)
{
    if (a.h == b.h) return a.w > b.w;
    return a.h > b.h;//确保h递减
}
ll dp[N][104];
ll dy(int j,int k,int t)
{
    return dp[j][t] - dp[k][t];
}
ll dx(int j,int k)
{
    return b[k+1].h - b[j+1].h;
}
int main()
{
    int n,k;
    while (Sint2(n,k) == 2)
    {
        for (int i = 1;i <= n;++i)
        {
            Sll2(b[i].w,b[i].h);
        }
        sort(b+1,b+n+1,cmp);
        int t = 1;
        for (int i = 1;i <= n;++i)
        {
            if (b[t].w < b[i].w) b[++t] = b[i];
        }
//        cout<<t<<endl;
        k = min(t,k);
        for (int i = 1;i <= t;++i) dp[i][1] = b[i].w*b[1].h;
        for (int j = 2;j <= k;++j)
        {
            head = tail = 0;mem(q,0);
            q[tail++] = 0;
            for (int i = 1;i <= t;++i)
            {
                while (head+1<tail&&dy(q[head+1],q[head],j-1) <= b[i].w * dx(q[head+1],q[head])) ++head;
                dp[i][j] = dp[q[head]][j-1] + b[i].w * b[q[head]+1].h;
                while (head+1<tail&&dy(i,q[tail-1],j-1)*dx(q[tail-1],q[tail-2]) <= dy(q[tail-1],q[tail-2],j-1)*dx(i,q[tail-1])) --tail;
                q[tail++] = i;
            }
        }
        ll ans = dp[t][1];
        for (int i = 2;i <= k;++i)
        {
            ans = min(ans,dp[t][i]);
        }
        Pllc(ans,‘\n‘);
    }
    return 0;
}

6.HDU 3045 Picnic Cows

#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;
/*
Cows in the same team should reduce their Moo~ to the one who has the lowest Moo~ in this team

    dp[i]表示前i头牛的最小花费
    dp[i] = dp[j] + (sum[i]-sum[j]-(i-j)*a[j+1])
    dp[j]-dp[k]+sum[k]-sum[j]+j*a[j+1]-k*a[k+1] < i*(a[j+1]-a[k+1])
*/
const int N = 400004;
ll dp[N],a[N],sum[N];
int q[N],head,tail;
ll dy(int j,int k)
{
    return dp[j]-dp[k] + sum[k]-sum[j] + j*a[j+1]-k*a[k+1];
}
ll dx(int j,int k)
{
    return a[j+1] - a[k+1];
}
ll DP(int i,int j)
{
    return dp[j] + (sum[i]-sum[j]-(i-j)*a[j+1]);
}
int main()
{
    int n,t;
    while (Sint2(n,t) == 2)
    {
        for (int i = 1;i <= n;++i) Sll(a[i]);
        sort(a+1,a+n+1);
        for (int i = 1;i <= n;++i) sum[i] = sum[i-1] + a[i];
        head = tail = 0;
        q[tail++] = 0;
        for (int i = 1;i <= n;++i)
        {
            while (head+1<tail&&dy(q[head+1],q[head]) <= i*dx(q[head+1],q[head])) ++head;
            dp[i] = DP(i,q[head]);
            int j = i-t+1;
            if (j < t) continue;
            while (head+1<tail&&dy(j,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(q[tail-1],q[tail-2])*dx(j,q[tail-1])) --tail;
            q[tail++] = j;
        }
        Pllc(dp[n],‘\n‘);
    }
    return 0;
}

7.HDU 3516 Tree Construction

斜率DP写的怎么都过不了,最后用四边形不等式,要保证j-i递增于是枚举长度

#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][j]表示从i到j所需的最小花费
    dp[i][j] = min {dp[i][k] + dp[k+1][j] + w(i,k,j)}
    w(i,k,j) = y[k] - y[j] + x[k+1] - x[i]
    s[i][j] = k 表示 dp[i][j]这个状态最优的决策是 k
*/
const int N = 1003;
const int inf = 0x3f3f3f3f;
int dp[N][N],s[N][N];
int x[N],y[N];
int w(int i,int k,int j)
{
    if (k >= j) return inf;
    return y[k] - y[j] + x[k+1] - x[i];
}
int DP(const int &n)
{
    mem(dp,0);int tmp;
    for (int L = 2;L<=n;++L) //以j-i递增为顺序递推
    {
        for (int i = 1,j = L;i+L-1<=n;++i,j = i+L-1)//i 是 区间左端点,j是区间右端点
        {
            dp[i][j] = inf;
            for (int k = s[i][j-1];k <= s[i+1][j];++k)
            {
                tmp = dp[i][k] + dp[k+1][j] + w(i,k,j);
                if (tmp < dp[i][j])
                {
                    dp[i][j] = tmp;
                    s[i][j] = k;
                }
            }
        }
    }
    return dp[1][n];
}
int main()
{
    int n;
    while (Sint(n) == 1)
    {
        for (int i = 1;i <= n;++i)
        {
            Sint2(x[i],y[i]);
            s[i][i] = i;
        }
        Pintc(DP(n),‘\n‘);
    }
    return 0;
}

8.POJ 1160 Post Office

状态转移方程写的好的话不用优化也可以过

这个题的状态转移的方程很经典啊

9.POJ 1180 Batch Scheduling

#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;
const int inf = 0x3f3f3f3f;
/*
    dp[i]表示前i个job的最小花费
    dp[i] = dp[j] + (S+sumt[i] - sumt[j]) *(sumf[i]-sumf[j])
*/
const int N = 10004;
ll dp[N];
ll sumt[N],sumf[N];
ll t[N],f[N];
int S,n;
int q[N],head,tail;
ll dy(int j,int k)
{
    return dp[j] - dp[k];
}
ll dx(int j,int k)
{
    return sumt[j] - sumt[k];
}
ll DP(int i,int j)
{
    return dp[j] + (S + sumt[i]-sumt[j])*sumf[i];
}
int main()
{
    while (Sint2(n,S) == 2)
    {
        for (int i = n;i >= 1;--i) Sll2(t[i],f[i]);
        for (int i = 1;i <= n;++i)
        {
            sumt[i] = sumt[i-1] + t[i];
            sumf[i] = sumf[i-1] + f[i];
        }
        head = tail = 0;
        q[tail++] = 0;
        for (int i = 1;i <= n;++i)
        {
            while (head+1<tail&&dy(q[head],q[head+1])>dx(q[head],q[head+1])*sumf[i]) ++head;
            dp[i] = DP(i,q[head]);
            while (head+1<tail&&dy(q[tail-1],i)*dx(q[tail-2],q[tail-1])<dy(q[tail-2],q[tail-1])*dx(q[tail-1],i)) --tail;
            q[tail++] = i;
        }
        Pllc(dp[n],‘\n‘);
    }
    return 0;
}

长路漫漫,其修远兮,ORZ

时间: 2024-10-07 12:42:43

【转】斜率优化DP和四边形不等式优化DP整理的相关文章

HDU 2829 Lawrence (斜率优化DP或四边形不等式优化DP)

题意:给定 n 个数,要你将其分成m + 1组,要求每组数必须是连续的而且要求得到的价值最小.一组数的价值定义为该组内任意两个数乘积之和,如果某组中仅有一个数,那么该组数的价值为0. 析:DP状态方程很容易想出来,dp[i][j] 表示前 j 个数分成 i 组.但是复杂度是三次方的,肯定会超时,就要对其进行优化. 有两种方式,一种是斜率对其进行优化,是一个很简单的斜率优化 dp[i][j] = min{dp[i-1][k] - w[k] + sum[k]*sum[k] - sum[k]*sum[

[dp专题-四边形不等式优化]51nod 1022

? 1021?石子归并?V1 N堆石子摆成一条线.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价.计算将N堆石子合并成一堆的最小代价. ? 例如: 1 2 3 4,有不少合并方法 1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19) 1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24) 1 2 3 4 => 1 2 7(7) => 3 7(1

合并石子 四边形不等式优化

题目描述 有一排石子,共n 堆.现要将石子有次序地合并成一堆.规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分.试设计一个算法,计算出将n堆石子合并成一堆的最小得分. 题解 首先由直接动态规划的方法来做,即 for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) for(int k=i;k<=j;k++) { f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+cost[j]-cost[i-1]);

BZOJ 1010 玩具装箱toy(四边形不等式优化DP)(HNOI 2008)

Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1...N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的.同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<

四边形不等式优化DP——石子合并问题 学习笔记

好方啊马上就要区域赛了连DP都不会QAQ 毛子青<动态规划算法的优化技巧>论文里面提到了一类问题:石子合并. n堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分. 求出将n堆石子合并成一堆的最小得分和最大得分以及相应的合并方案. 设m[i,j]表示合并d[i..j]所得到的最小得分. 状态转移方程: 总的时间复杂度为O(n3). [优化方案] 四边形不等式: m[i,j]满足四边形不等式 令s[i,j]=max{k | m[

hdu 3480 dp 四边形不等式优化

http://acm.hdu.edu.cn/showproblem.php?pid=3480 给出一个数字集合 S,大小为 n,要求把这个集合分成m个子集,每分出一个子集的费用是子集中的 (max-min)^2,求最小费用. 开始的dp转移很容易想到. 首先对集合从小到大排序,dp[i][j] 表示前i个元素被分成j个子集的最小费用.然后枚举最后一个子集. dp[i][j] = min{dp[k-1][j-1] + cost(k, i)}; 这个转移明显是过不去的,n<10000 m<5000

石子合并(四边形不等式优化dp)

该来的总是要来的———————— 经典问题,石子合并. 对于 f[i][j]= min{f[i][k]+f[k+1][j]+w[i][j]} From 黑书 凸四边形不等式:w[a][c]+w[b][d]<=w[b][c]+w[a][d](a<b<c<d) 区间包含关系单调: w[b][c]<=w[a][d](a<b<c<d) 定理1:  如果w同时满足四边形不等式和决策单调性 ,则f也满足四边形不等式 定理2:  若f满足四边形不等式,则决策s满足 s[i

pku 1160 Post Office 四边形不等式优化 经典DP

pku 1160 Post Office 四边形不等式优化 经典DP 邮局 经典的动态规划问题,主要体现在状态的设计和可以用四边形不等式优化上 题意是:给你n个村庄,然后让你用m个邮局对这些村庄进行覆盖,然后让你设计覆盖方式使得每个村庄到其对应邮局的路程和最短 本题状态的设计的灵感来源于"覆盖"这个点,最优子结构其实就是用 m 个邮局覆盖,以及用 m-1个邮局覆盖 那么,状态为dp[n][m] 为前 n 个村庄用 m 个邮局进行覆盖使得每个村庄到其对应的邮局的最短路程和 转移方程:dp

四边形不等式优化dp

对四边形不等式优化dp的理解 四边形不等式适用于优化最小代价子母树问题,即f[i][j]=max/min(f[i][k-1]+f[k][j])+w[i][j],类似枚举中间点的dp问题,典型例题石子归并; 如果w函数满足区间包含的单调性和四边形不等式,那么函数f也满足四边形不等式,如果f满足四边形不等式,s[i][j]=max/min{t|f[i][j]=f[i][k-1]+f[k][j]}+w[i][j],也就是s[i][j]是f[i][j]取得最优解的中间点,s[i][j]具有单调性; 即s