zoukankan      html  css  js  c++  java
  • [转]四边形不等式优化dp(POJ1160)

    四边形不等式优化动态规划原理:

    1.当决策代价函数w[i][j]满足w[i][j]+w[i’][j’]<=w[I;][j]+w[i][j’](i<=i’<=j<=j’),w满足四边形不等式.当函数w[i][j]满足w[i’][j]<=w[i][j’] i<=i’<=j<=j’),w关于区间包含关系单调.

    2.如果状态转移方程m且决策代价w满足四边形不等式的单调函数(可以推导出m亦为满足四边形不等式的单调函数),则可利用四边形不等式推出最优决策s的单调函数性,从而减少每个状态的状态数,将算法的时间复杂度由原来的O(n^3)降低为O(n^2).方法是通过记录子区间的最优决策来减少当前的决策量.:

    s[i][j]=max{k | ma[i][j] = m[i][k-1] + m[k][j] + w[i][j]}

    由于决策s具有单调性,因此状态转移方程可修改为:

     

    证明过程: (转载)

    m[i,j]表示动态规划的状态量。

    m[i,j]有类似如下的状态转移方程:

    m[i,j]=opt{m[i,k]+m[k,j]}(ikj)

    如果对于任意的abcd,有m[a,c]+m[b,d]m[a,d]+m[b,c],那么m[i,j]满足四边形不等式。

    以上是适用这种优化方法的必要条件

    对于一道具体的题目,我们首先要证明它满足这个条件,一般来说用数学归纳法证明,根据题目的不同而不同。

    通常的动态规划的复杂度是O(n3),我们可以优化到O(n2)

    s[i,j]m[i,j]的决策量,即m[i,j]=m[i,s[i,j]]+m[s[i,j]+j]

    我们可以证明,s[i,j-1]s[i,j]s[i+1,j]  (证明过程见下)

    那么改变状态转移方程为:

    m[i,j]=opt{m[i,k]+m[k,j]}      (s[i,j-1]ks[i+1,j])

    复杂度分析:不难看出,复杂度决定于s的值,以求m[i,i+L]为例,

    (s[2,L+1]-s[1,L])+(s[3,L+2]-s[2,L+1])…+(s[n-L+1,n]-s[n-L,n-1])=s[n-L+1,n]-s[1,L]n

    所以总复杂度是O(n2)

    s[i,j-1]s[i,j]s[i+1,j]的证明:

    mk[i,j]=m[i,k]+m[k,j]s[i,j]=d

    对于任意k<d,有mk[i,j]md[i,j](这里以m[i,j]=min{m[i,k]+m[k,j]}为例,max的类似),接下来只要证明mk[i+1,j]md[i+1,j],那么只有当s[i+1,j]s[i,j]时才有可能有ms[i+1,j][i+1,j]md[i+1,j]

    (mk[i+1,j]-md[i+1,j]) - (mk[i,j]-md[i,j])

    =(mk[i+1,j]+md[i,j]) - (md[i+1,j]+mk[i,j])

    =(m[i+1,k]+m[k,j]+m[i,d]+m[d,j]) - (m[i+1,d]+m[d,j]+m[i,k]+m[k,j])

    =(m[i+1,k]+m[i,d]) - (m[i+1,d]+m[i,k])

    m满足四边形不等式,∴对于i<i+1k<dm[i+1,k]+m[i,d]m[i+1,d]+m[i,k]

    (mk[i+1,j]-md[i+1,j])(mk[i,j]-md[i,j])0

    s[i,j]s[i+1,j],同理可证s[i,j-1]s[i,j]

    证毕

    扩展:

    以上所给出的状态转移方程只是一种比较一般的,其实,很多状态转移方程都满足四边形不等式优化的条件。

    解决这类问题的大概步骤是:

    0.证明w满足四边形不等式,这里wm的附属量,形如m[i,j]=opt{m[i,k]+m[k,j]+w[i,j]},此时大多要先证明w满足条件才能进一步证明m满足条件

    1.证明m满足四边形不等式

    2.证明s[i,j-1]s[i,j]s[i+1,j]

    pku 1160 Post Office 解题报告

    题意: 给出m个村庄及其距离,给出n个邮局,要求怎么建n个邮局使代价最小.

    算法:很显然用到动态规划,那么假设:

    d[i…n],各邮局的坐标

    w[i][j]表示在d[i][j]之间建立一个邮局的村庄为k,kij之和的一半(很显然在中间建一个邮局距离最小),那么

    m[i][j]为在前j个村庄建立i个邮局的最小距离和.

    那么状态转移方程为:

    边界条件: m[1][j]=w[1][j]  (1<=j<=m)

    状态转移方程

    那么思路则为:

    for i=2 to p do      //递推邮局数

    {

         //m:在前j个村庄建立i个邮局的最小距离和

         for j=n dwonto i+1 do    //按递减顺序枚举尾指针

         m[i][j]=inf;

         for k=1 to n do

         {

              temp = m[i-1][k]+calcw(k+1,j);

              if(temp<m[i][j]) m[i][j]=temp;

         }

    }

    这样时间复杂度显然为O(n^3),这是不能接受的

    仔细分析这dp算法,关键是决策变量k枚举数太多联系到四边形不等式原理,w[i][j]m[i][j]很明显符合四边形不等式,我们假设决策变量s[i][j],如果在110的村庄中,建立1个邮局的最佳位置为8,那么在决定见多一个邮局的话,当然是在18之间了(根据四边形不等式原理猜想到),所以就在dp的过程中,s[i][j]记录前i-1个邮局的村庄数那么我们第三次搜索的时候,就需要根据决策表s[i-1][j]<=k<=s[i][j+1]的范围内枚举.而可以证明s[i][j]具有单调性,那么我们就可以利用s[i][j]单调性限制了上下界然后把 O(n^3)弄成了 O(n^2) 

    sample为例:

    状态方程m:

     

    决策表s:

     那么状态转移方程为:

    边界条件: m[1][j]=w[1][j]  (1<=j<=m)

    边界条件: m[1][j]=w[1][j]  (1<=j<=m)

    状态转移方程

    决策记录表: s[i][j]=k

    代码:

    View Code
    //#pragma comment(linker,"/STACK:327680000,327680000")
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <functional>
    #include <numeric>
    #include <sstream>
    #include <stack>
    #include <map>
    #include <queue>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   (l + r) >> 1
    #define Min(x, y)   (x) < (y) ? (x) : (y)
    #define Max(x, y)   (x) < (y) ? (y) : (x)
    #define E(x)        (1 << (x))
    #define iabs(x)     (x) < 0 ? -(x) : (x)
    #define OUT(x)  printf("%I64d\n", x)
    #define Read()  freopen("data.in", "r", stdin)
    #define Write() freopen("data.out", "w", stdout);
    
    typedef long long LL;
    const double eps = 1e-8;
    const double PI = acos(-1.0);
    const int inf = ~0u>>2;
    
    using namespace std;
    
    const int N = 310;
    const int M = 33;
    
    int dp[N][N];
    int s[N][N];
    int d[N];
    int sum[N][N];
    
    
    int dis(int i, int j) {
        if(i >= j)  return 0;
        if(sum[i][j] != 0)  return sum[i][j];
        int a = i, b = j;
        while(a < b) {
            sum[i][j] += (d[b--] - d[a++]);
        }
        return sum[i][j];
    }
    
    int main() {
        //Read();
    
        int i, j, k, V, P, tmp;
        while(~scanf("%d%d", &V, &P)) {
            for(i = 1; i <= V; ++i) {
                scanf("%d", d + i);
            }
            CL(sum, 0);
            CL(dp, 0);
            for(i = 1;  i <= V; ++i) {
                dp[1][i] = dis(1, i);
                s[i][i] = i-1;
            }
            for(i = 2; i <= P; ++i) {
                s[i][V+1] = V-1;
                for(j = V; j >= i; --j) {
                    dp[i][j] = inf;
                    for(k = s[i-1][j]; k <= s[i][j+1]; ++k) {
                        tmp = dp[i-1][k] + dis(k + 1, j);
                        if(tmp < dp[i][j]) {
                            dp[i][j] = tmp;
                            s[i][j] = k;
                        }
                    }
                }
            }
            printf("%d\n", dp[P][V]);
        }
        return 0;
    }

     

     

     

     

     

     

  • 相关阅读:
    svn 搭建及环境测试 详细
    mysql 开启用户远程登录
    Linux svn 搭建
    Subversion(svn) 简介
    pytest快速入门(1)--pytest与unittest的比较总揽
    python自动化测试(8)--json字符串与dict之间的相互转换
    web自动化测试(2)--使用xpath实现页面元素的定位
    web自动化测试(1)--web页面元素的8种定位方法
    性能测试入门(1)--性能测试关键指标
    python自动化测试(6)--测试流程mark
  • 原文地址:https://www.cnblogs.com/vongang/p/2869315.html
Copyright © 2011-2022 走看看