zoukankan      html  css  js  c++  java
  • 四边形优化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)
    #define WS(s) printf("%s
    ", 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
    ", 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)
    #define WS(s) printf("%s
    ", 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 ", 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)
    #define WS(s) printf("%s
    ", 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 ", dp[n][m]); } return 0; }




  • 相关阅读:
    Spring MVC程序中得到静态资源文件css,js,图片文件的路径问题总结
    【转】MySQL数据丢失讨论
    【摘】 pt-query-digest工具一解
    【摘】Mysql备份还原数据库之mysqldump实例及参数详细说明
    【原】redis插件安装
    【摘】linux中fstab解说
    【原】mysql慢日志分析
    【原】带宽、流量单位换算关系
    【转】Linux查看内存大小和插槽
    【转】bind
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/6727207.html
Copyright © 2011-2022 走看看