四边形优化dp

来源:互联网 发布:淘宝首页设计欣赏 编辑:程序博客网 时间:2024/06/04 23:23

理解:

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



1 0