四边形优化dp

理解:

http://blog.renren.com/share/263498909/1064362501

http://www.cnblogs.com/ronaflx/archive/2011/03/30/1999764.html

http://yomean.blog.163.com/blog/static/189420225201272864127683/

http://www.cnblogs.com/zxndgv/archive/2011/08/02/2125242.html

题目总结:

http://www.cnblogs.com/ronaflx/archive/2011/03/30/1999764.html

下摘自:http://www.cnblogs.com/zxndgv/archive/2011/08/02/2125242.html

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

最有代价用d[i,j]表示

d[i,j]=min{d[i,k-1]+d[k+1,j]}+w[i,j]

当中w[i,j]=sum[i,j]

四边形不等式

w[a,c]+w[b,d]<=w[b,c]+w[a,d](a<b<c<d) 就称其满足凸四边形不等式

决策单调性

w[i,j]<=w[i‘,j‘]   ([i,j]属于[i‘,j‘]) 既 i‘<=i<j<=j‘

于是有下面三个定理

定理一: 假设w同一时候满足四边形不等式 和 决策单调性 ,则d也满足四边形不等式

定理二:当定理一的条件满足时,让d[i,j]取最小值的k为K[i,j],则K[i,j-1]<=K[i,j]<=K[i+1,j]

定理三:w为凸当且仅当w[i,j]+w[i+1,j+1]<=w[i+1,j]+w[i,j+1]

由定理三知 推断w是否为凸即推断 w[i,j+1]-w[i,j]的值随着i的添加是否递减

于是求K值的时候K[i,j]仅仅和K[i+1,j] 和 K[i,j-1]有关。所以 能够以i-j递增为顺序递推各个状态值终于求得结果  将O(n^3)转为O(n^2)

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

注意:

注意决策单调性顺序,既要符合决策调性,也要符合题意 (!!!!)

注意枚举顺序,依据dp方程和决策单调性方程

注意初始化,防止訪问到无效状态或没处理的状态。

dp和s边界初始化,尤其是决策的上届和下届初始化。

例题1:

石子合并问题:hdu 3506

dp[i][j] = min{dp[i][k] + dp[k + 1][j] + cost[i, j] }, i <= k <= j - 1 , cost[i][j] = sum[j] - sum[i - 1]

s[i][j - 1] <= s[i][j] <= s[i + 1][j]

//#pragma warning (disable: 4786)
//#pragma comment (linker, "/STACK:16777216")
//HEAD
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <string>
#include <set>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
//LOOP
#define FE(i, a, b) for(int i = (a); i <= (b); ++i)
#define FD(i, b, a) for(int i = (b); i>= (a); --i)
#define REP(i, N) for(int i = 0; i < (N); ++i)
#define CLR(A,value) memset(A,value,sizeof(A))
#define CPY(a, b) memcpy(a, b, sizeof(a))
#define FC(it, c) for(__typeof((c).begin()) it = (c).begin(); it != (c).end(); it++)
//INPUT
#define RI(n) scanf("%d", &n)
#define RII(n, m) scanf("%d%d", &n, &m)
#define RIII(n, m, k) scanf("%d%d%d", &n, &m, &k)
#define RS(s) scanf("%s", s)
//OUTPUT
#define WI(n) printf("%d\n", n)
#define WS(s) printf("%s\n", s)

typedef long long LL;
const int INF = 1000000007;
const double eps = 1e-10;
const int maxn = 2010;

int dp[maxn][maxn], s[maxn][maxn];
int w[maxn][maxn];

int n, m;
int val[maxn];
int sum[maxn];

///求dp最小值
///枚举 区间 由小到大
void solve()
{
//    memset(dp, 0, sizeof(dp));///初始化无效值
    FE(i, 1, 2 * n)
    {
        dp[i][i] = 0;
        s[i][i] = i;/// 初始化决策下届,为0
    }
    FE(len, 2, n)
    {
        for (int i = 2 * n - len; i >= 1; i--)
        {
            int j = i + len - 1;

            dp[i][j] = INF;
            int a = s[i][j - 1], b = s[i + 1][j];
            int cost = sum[j] - sum[i - 1];
            for (int k = a; k <= b; k++)
            {
                if (dp[i][j] > dp[i][k] + dp[k + 1][j] + cost)
                {
                    dp[i][j] = dp[i][k] + dp[k + 1][j] + cost;
                    s[i][j] = k;
                }
            }
        }
    }
}

int main ()
{
    while (~RI(n))
    {
        FE(i, 1, n) RI(val[i]), val[i + n] = val[i];;
        FE(i, 1, 2 * n) sum[i] = sum[i - 1] + val[i];
//        pre();
        solve();
        int ans = INF;
        FE(i, 1, n)
        {
            if (ans > dp[i][n + i - 1])
                ans = dp[i][n + i - 1];
        }
        printf("%d\n", ans);
    }
    return 0;
}

例题2:邮局问题:

poj 1160

1:注意此法的 i 和 j 顺序与寻常不同

此时决策区间为:s[i - 1][j] <= s[i][j] <= s[i][j + 1] (!!!)

详细见凝视:

//#pragma warning (disable: 4786)
//#pragma comment (linker, "/STACK:16777216")
//HEAD
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <string>
#include <set>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
//LOOP
#define FE(i, a, b) for(int i = (a); i <= (b); ++i)
#define FD(i, b, a) for(int i = (b); i>= (a); --i)
#define REP(i, N) for(int i = 0; i < (N); ++i)
#define CLR(A,value) memset(A,value,sizeof(A))
#define CPY(a, b) memcpy(a, b, sizeof(a))
#define FC(it, c) for(__typeof((c).begin()) it = (c).begin(); it != (c).end(); it++)
//INPUT
#define RI(n) scanf("%d", &n)
#define RII(n, m) scanf("%d%d", &n, &m)
#define RIII(n, m, k) scanf("%d%d%d", &n, &m, &k)
#define RS(s) scanf("%s", s)
//OUTPUT
#define WI(n) printf("%d\n", n)
#define WS(s) printf("%s\n", s)

typedef long long LL;
const int INF = 1000000007;
const double eps = 1e-10;
const int maxn= 1000010;

int dp[33][333], s[33][333];
int w[333][333];

int n, m;
int val[333];
int sum[333];
///dp[i][j] = min{dp[i - 1][k] + w[k + 1][j]}, i - 1 <= k <= j - 1
///一般要求 i <= j (!!)
///s[i - 1][j] <= s[i][j] <= s[i][j + 1] (!

!

)
///注意决策单调性顺序,既要符合决策调性,也要符合题意 (!!!!)
///注意枚举顺序,依据dp方程和决策单调性方程
///注意初始化,防止訪问到无效状态或没处理的状态。dp和s边界初始化。尤其是决策的上届和下届初始化。

void pre()
{
    for(int i = 1; i <= n; i ++) //这里有一个递推公式能够进行预处理
    {
        w[i][i] = 0;
        for(int j = i + 1; j <= n; j ++)
        {
            int mid = (j + i) >> 1;
            w[i][j] = w[i][j - 1] + val[j] - val[mid];
//            int x = sum[j] - sum[mid] - val[mid] * (j - mid);
//            x += val[mid] * (mid - i) - (sum[mid - 1] - sum[i - 1]);
        }
    }
}

///求dp最小值
///枚举i从小到大
///再枚举j从大到小
void solve()
{
    memset(dp, 0, sizeof(dp));///初始化无效值
    FE(i, 1, n)
    {
        dp[1][i] = w[1][i];
        s[1][i] = 0;/// 初始化决策下届,为0
    }
    FE(i, 2, m)
    {
        //s[1][i] = 0;
        s[i][n + 1] = n;///初始化决策上届
        for (int j = n; j >= i; j--)
        {
            int tmp = dp[i][j] = INF;///初始化最优值
            int a = s[i - 1][j], b = s[i][j + 1];
            //a = max(a, i - 1); b = min(b, j - 1); // i - 1 <= k <= j - 1
            for (int k = a; k <= b; k++) ///保证枚举到的都是有效状态,且都已计算过
            {
                if (tmp > dp[i - 1][k] + w[k + 1][j])
                {
                    tmp = dp[i - 1][k] + w[k + 1][j];
                    s[i][j] = k;
                }
            }
            dp[i][j] = tmp;
        }
    }
}

int main ()
{
    while (~RII(n, m))
    {
        FE(i, 1, n) RI(val[i]), sum[i] = sum[i - 1] + val[i];
        pre();
        solve();
        printf("%d\n", dp[m][n]);
    }
    return 0;
}

2:-详细见凝视

此时决策区间为:s[i][j - 1] <= s[i][j] <= s[i + 1][j] (!!!)

//#pragma warning (disable: 4786)
//#pragma comment (linker, "/STACK:16777216")
//HEAD
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <string>
#include <set>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
//LOOP
#define FE(i, a, b) for(int i = (a); i <= (b); ++i)
#define FD(i, b, a) for(int i = (b); i>= (a); --i)
#define REP(i, N) for(int i = 0; i < (N); ++i)
#define CLR(A,value) memset(A,value,sizeof(A))
#define CPY(a, b) memcpy(a, b, sizeof(a))
#define FC(it, c) for(__typeof((c).begin()) it = (c).begin(); it != (c).end(); it++)
//INPUT
#define RI(n) scanf("%d", &n)
#define RII(n, m) scanf("%d%d", &n, &m)
#define RIII(n, m, k) scanf("%d%d%d", &n, &m, &k)
#define RS(s) scanf("%s", s)
//OUTPUT
#define WI(n) printf("%d\n", n)
#define WS(s) printf("%s\n", s)

typedef long long LL;
const int INF = 1000000007;
const double eps = 1e-10;
const int maxn= 1000010;

int dp[333][33], s[333][33];
int w[333][333];

int n, m;
int val[333];
int sum[333];
///dp[i][j] = min{dp[k][j - 1] + w[k + 1][j]}, j - 1 <= k <= i - 1
///此处i>=j
///s[i][j - 1] <= s[i][j] <= s[i + 1][j] (!!

)
///注意决策单调性顺序,既要符合决策调性,也要符合题意 (!!!!)
///注意枚举顺序。依据dp方程和决策单调性方程
///注意初始化。防止訪问到无效状态或没处理的状态。

dp和s边界初始化,尤其是决策的上届和下届初始化。

void pre()
{
    for(int i = 1; i <= n; i ++) //这里有一个递推公式能够进行预处理
    {
        w[i][i] = 0;
        for(int j = i + 1; j <= n; j ++)
        {
            int mid = (j + i) >> 1;
            w[i][j] = w[i][j - 1] + val[j] - val[mid];
        }
    }
}

///求dp最小值
///枚举i从小到大
///再枚举j从大到小
void solve()
{
    memset(dp, 0, sizeof(dp));///初始化无效值
    FE(i, 1, n)
    {
        dp[i][1] = w[1][i];
        s[i][1] = 0;/// 初始化决策下届,为0
    }
    FE(i, 2, m)
    {
        //s[i][1] = 0;
        s[n + 1][i] = n;///初始化决策上届
        for (int j = n; j >= i; j--)
        {
            int tmp = dp[j][i] = INF;///初始化最优值
            int a = s[j][i - 1], b = s[j + 1][i];
            //a = max(a, i - 1); b = min(b, j - 1);
            for (int k = a; k <= b; k++) ///保证枚举到的都是有效状态,且都已计算过
            {
                if (tmp > dp[k][i - 1] + w[k + 1][j])
                {
                    tmp = dp[k][i - 1] + w[k + 1][j];
                    s[j][i] = k;
                }
            }
            dp[j][i] = tmp;
        }
    }
}

int main ()
{
    while (~RII(n, m))
    {
        FE(i, 1, n) RI(val[i]);
        pre();
        solve();
        printf("%d\n", dp[n][m]);
    }
    return 0;
}
时间: 2024-10-18 08:12:47

四边形优化dp的相关文章

HDOJ 3516 Tree Construction 四边形优化dp

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=3516 题意: 大概就是给你个下凸包的左侧,然后让你用平行于坐标轴的线段构造一棵树,并且这棵树的总曼哈顿距离最短 题解: 很容易得到转移方程: $$dp[i][j]=min \{ dp[i][k-1]+dp[k][j] + dis(uni(i,k-1),uni(k,j))\}$$ 其中$dp[i][j]$表示从$i$到$j$的最优解,$dis(i,j)$表示$i$和$j$之间的曼哈顿距离,$uni(i

四边形优化DP学习

转自:http://www.cnblogs.com/hadilo/p/5800306.html 在动态规划中,经常遇到形如下式的状态转移方程: m(i,j)=min{m(i,k-1),m(k,j)}+w(i,j)(i≤k≤j)(min也可以改为max) 上述的m(i,j)表示区间[i,j]上的某个最优值.w(i,j)表示在转移时需要额外付出的代价.该方程的时间复杂度为O(N3) 下面我们通过四边形不等式来优化上述方程,首先介绍什么是"区间包含的单调性"和"四边形不等式&quo

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)

该来的总是要来的———————— 经典问题,石子合并. 对于 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

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的四边形优化 一.进行四边形优化需要满足的条件 1.状态转移方程如下: m(i,j)表示对应i,j情况下的最优值. w(i,j)表示从i到j的代价. 例如在合并石子中: m(i,j)表示从第i堆石子合并到j堆石子合并成一堆的最小代价. w(i,j)表示从第i堆石子到第j堆石子的重量和. 2.函数w满足区间包含的单调性和四边形不等式 二.满足上述条件之后的两条定理 1.假如函数w满足上述条件,那么函数m 也满足四边形不等式,即 例如: 假如有:w(1, 3) + w(2, 4) £ w(2,

四边形不等式优化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

HDU 3506 (环形石子合并)区间dp+四边形优化

Monkey Party Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 1699    Accepted Submission(s): 769 Problem Description Far away from our world, there is a banana forest. And many lovely monkeys l

【转】斜率优化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主要的原理就是利用斜率或者四边形不等式等数学方法 在所有要判断的子状态中迅速做出判断,所以这里的优化其实是省去了枚举