平行四边形优化

 

平行四边形优化(HDOJ3506)

  1034人阅读  评论(0)  收藏  举报
  分类:

四边形不等式是一种比较常见的优化动态规划的方法:

证明:http://baike.baidu.com/view/1985058.htm?fr=aladdin

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

状态转移方程 dp[i][j]=min{dp[i][k-1]+dp[k][j]}+w[i][j]  (i<=k<=j) 

要用平行四边形优化则要证明w[i][j],dp[i][j]是否满足四边形不等式


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

或打表观察w[i][j+1]-w[i][j]关于i的表达式,如果关于i递减,则w满足凸四边形不等式

如果一个函数w[i][j],满足 w[i'][j]<=w[i][j']  i<=i'<=j<=j' 则称w关于区间包含关系单调

如果w同时满足四边形不等式和区间单调关系,则dp也满足四边形不等式


通常的动态规划的复杂度是O(n^3),四边形不等式程序中跑一遍i只会跑一遍j,所以可以优化到O(n^2)



详细证明:

http://baike.baidu.com/view/1985058.htm?fr=aladdin

http://blog.youkuaiyun.com/lmyclever/article/details/6677683


经典的石子合并问题:http://blog.youkuaiyun.com/acdreamers/article/details/18039073


HDOJ2829

题目大意:给定一个长度为n的序列,至多将序列分成m段,每段序列都有权值,权值为序列内两个数两两相乘之和。m<=n<=1000. 令权值最小。

状态转移方程:

dp[c][i]=min(dp[c][i],dp[c-1][j]+w[j+1][i])

[cpp]  view plain  copy
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. using namespace std;  
  5. const int INF=1<<30;  
  6. const int MAXN=1000+10;  
  7. typedef long long LL;  
  8. LL dp[MAXN][MAXN];//dp[c][j]表示前j个点切了c次后的最小权值  
  9. int val[MAXN];  
  10. int w[MAXN][MAXN];//w[i][j]表示i到j无切割的权值  
  11. int s[MAXN][MAXN];//s[c][j]表示前j个点切的第c次的位置  
  12. int sum[MAXN];  
  13. int main()  
  14. {  
  15.     int n,m;  
  16.     while(~scanf("%d%d",&n,&m))  
  17.     {  
  18.         if(n==0&&m==0)break;  
  19.         memset(s,0,sizeof(s));  
  20.         memset(w,0,sizeof(w));  
  21.         memset(dp,0,sizeof(dp));  
  22.         memset(sum,0,sizeof(sum));  
  23.         for(int i=1;i<=n;++i)  
  24.         {  
  25.             scanf("%d",&val[i]);  
  26.             sum[i]+=sum[i-1]+val[i];  
  27.         }  
  28.         for(int i=1;i<=n;++i)  
  29.         {  
  30.             w[i][i]=0;  
  31.             for(int j=i+1;j<=n;++j)  
  32.             {  
  33.                 w[i][j]=w[i][j-1]+val[j]*(sum[j-1]-sum[i-1]);  
  34.             }  
  35.         }  
  36.         for(int i=1;i<=n;++i)  
  37.         {  
  38.             for(int j=1;j<=m;++j)  
  39.             {  
  40.                 dp[j][i]=INF;  
  41.             }  
  42.         }  
  43.         for(int i=1;i<=n;++i)  
  44.         {  
  45.             dp[0][i]=w[1][i];  
  46.             s[0][i]=0;  
  47.         }  
  48.         for(int c=1;c<=m;++c)  
  49.         {  
  50.             s[c][n+1]=n;//设置边界  
  51.             for(int i=n;i>c;--i)  
  52.             {  
  53.                 int tmp=INF,k;  
  54.                 for(int j=s[c-1][i];j<=s[c][i+1];++j)  
  55.                 {  
  56.                     if(dp[c-1][j]+w[j+1][i]<tmp)  
  57.                     {  
  58.                         tmp=dp[c-1][j]+w[j+1][i];//状态转移方程,j之前切了c-1次,第c次切j到j+1间的  
  59.                         k=j;  
  60.                     }  
  61.                 }  
  62.                 dp[c][i]=tmp;  
  63.                 s[c][i]=k;  
  64.             }  
  65.         }  
  66.         printf("%d\n",dp[m][n]);  
  67.     }  
  68.     return 0;  
  69. }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值