HDU 3669 Cross the Wall

很久没有来这个Blog写东西了。之前因为决定去腾讯,所以竞赛的事情放下了很长一段时间,现在重新拾起。

zhk大神要准备为WF拼一次,我和Talker与他并肩奋斗,于是zhk给我们推荐了一些题目去做,于是,就有了这个做到泪奔的Cross the Wall。

闲话少说,该题目为DP中的战斗机——斜率优化,在最简单的斜率优化基础上添加了一维K,总的来说思想还是没太大变化。之前上网找了很多资料,也去各位大神的Blog取过经,花了一周的时间才断断续续地搞清楚。下面就来简单总结一下自己这段时间学习斜率优化的心得。

 

1.DP方程

斜率优化的典型DP方程为dp(i) = min{dp(j) + f(i,j)},对于这道题,f(i,j) = h(j+1)*w(i)。加上挖洞的个数k,完整的DP方程为dp(i, k) = min{dp(j, k-1) + h(j+1) * w(i)};由于dp(i, k)只与dp(j, k-1)有关,则可以使用滚动数组,在此将DP方程简化为dp(i) = min{dp(j) + h(j+1) * w(i)};

若dp(i) == dp(j) + h(j+1) * w(i) (即当j时,能使dp(i, k)最小),则对于j'(0 <= j' < i && j' != j),有dp(j) + h(j+1) * w(i) < dp(j') + h(j'+1) * w(i);移项可得dp(j) - dp(j') < w(i) * (h(j'+1) - h(j+1));把h作为x,dp作为y,则w为斜率。至此,斜率优化的雏形初现。

 

2.决策点维护

由dp(j) - dp(j') < w(i) * (h(j'+1) - h(j+1))分析以及题目需要求最小值可知,决策点必然是所有可能作为决策点的点集中最靠近下方的,换句话说就是要求这个点集的左链即凸包的左半部分。至于这个决策点按照什么顺序产生,如何维护,都是大同小异的。要注意的是,h和w是有单调性的。在这道题中,我是保证h单减,w单增。

用堆栈p来维护左链,这样,若dp(i) = dp(j) + h(j+1) * w(i), (j < i),则在决策点的堆栈中,p(idx)作为决策点,有p(idx) = (h(j+1), dp(j));根据斜率优化的表达式以及凸包的性质可知,在堆顶元素之前,线段的斜率都是小于w(i)的。由于w是递增的,那么在计算dp(i+1)时,决策点不会退化到当前决策点p(idx)之前,这样就可以更快地找到决策点,降低复杂度。

 

3.实例

在分析算法的时候,自己写了个简单的数据来帮助理解。

4 100
99 100
100 99
200 50
201 49

则h[] = {100, 99, 50, 49}, w[] = {99, 100, 200, 201};

 

具体的就不细讲了。写这篇东西一来是自己总结,二来是希望能够给需要的人一些帮助。这道题dp值可能很大,要用int64,在输出的时候要用%I64d,否则会出错。就是这里我用%lld,让我查了一个下午...

太久不写代码始终是有些生疏了,很多问题需要注意,加油吧~

 

ExpandedBlockStart.gif HDU 3669 Cross the Wall
  1  #include  < stdio.h >
  2  #include  < string .h >
  3  #include  < algorithm >
  4 
  5  #define  DEBUG 0
  6 
  7  using   namespace  std;
  8 
  9  const   int  MaxN  =   50010 , MaxK  =   110 ;
 10  typedef  long   long  i64;
 11  int  N, K;
 12 
 13  struct  Rect {
 14       int  W, H;
 15  }rect[MaxN + 10 ];
 16 
 17  struct  Point {
 18      i64 x, y;
 19      Point() {}
 20      Point( const  i64 X,  const  i64 Y): x(X), y(Y) {}
 21      Point  operator   -  ( const  Point P)  const  {
 22           return  Point(x - P.x, y - P.y);
 23      }
 24      Point  operator   +  ( const  Point P)  const  {
 25           return  Point(x + P.x, y + P.y);
 26      }
 27  };
 28 
 29  i64 mul(Point a, Point b) {
 30       return  a.x * b.y  -  a.y * b.x;
 31  }
 32 
 33  struct  Line {
 34       int  sp, i;
 35      Point point[MaxN + 10 ];
 36      Line(){}
 37       void  init() {
 38          sp  =   - 1 ;
 39          i  =   0 ;
 40      }
 41       bool  insert(Point P) {
 42           while (sp  >   0   &&  mul(P - point[sp], P - point[sp - 1 ])  <   0 )     // 维护凸包
 43              sp -- ;
 44          point[ ++ sp]  =  P;
 45           return   true ;
 46      }
 47      i64 optimize( int  k) {
 48           while (i  <  sp  &&  point[i].y  +  point[i].x * >=  point[i + 1 ].y  +  point[i + 1 ].x * k)     // 斜率优化
 49              i ++ ;
 50           return  point[i].y  +  point[i].x * k;
 51      }
 52  }line;
 53 
 54  int  x[MaxN], y[MaxN], yn;
 55 
 56  int  ID( int  num) {
 57       int  s  =   - 1 , e  =  yn, mid  =  (s  +  e)  >>   1 ;
 58       while (s  +   1   <  e) {
 59           if (y[mid]  ==  num)
 60               return  mid;
 61           if (y[mid]  >  num)
 62              e  =  mid;
 63           else
 64              s  =  mid;
 65          mid  =  (s  +  e)  >>   1 ;
 66      }
 67  }
 68 
 69  void  optimize() {
 70       for ( int  i  =   1 ; i  <=  N;  ++ i)
 71          y[i - 1 =  rect[i].W;
 72      sort(y, y  +  N);
 73      yn  =  unique(y, y  +  N)  -  y;
 74      memset(x,  0 sizeof (x));
 75       for ( int  i  =   1 ; i  <=  N;  ++ i) {
 76           int  idx  =  ID(rect[i].W);
 77          x[idx]  =  max(x[idx], rect[i].H);
 78      }
 79      N  =  yn;
 80       for ( int  i  =   0 ; i  <  yn;  ++ i) {
 81          rect[i + 1 ].H  =  x[i];
 82          rect[i + 1 ].W  =  y[i];
 83      }
 84       int  sp  =   1 ;
 85       for ( int  i  =   1 ; i  <=  N;  ++ i) {
 86           if (sp  ==   0 ) {
 87              rect[ ++ sp]  =  rect[i];
 88               continue ;
 89          }
 90           if (rect[i].W  ==  rect[sp].W)
 91               continue ;
 92           if (rect[i].H  >=  rect[sp].H) {
 93              sp -- ; i -- ;
 94          }
 95           else   if (rect[i].H  <  rect[sp].H)
 96              rect[ ++ sp]  =  rect[i];
 97      }
 98      N  =  sp;
 99  #if  DEBUG
100       for ( int  i  =   1 ; i  <=  N;  ++ i)
101          printf( " %d %d\n " , rect[i].W, rect[i].H);
102  #endif
103  }
104 
105  i64 dp[ 2 ][MaxN + 10 ];
106 
107  void  work() {
108      optimize();             // 预处理所有矩形
109       int  cur  =   1 , pre  =   0 ;
110       for ( int  i  =   1 ; i  <=  N;  ++ i) {     // 初始化dp[pre]为挖一个洞的花费
111          dp[pre][i]  =  1LL * rect[i].W  *  rect[ 1 ].H;
112      }
113       int  cnt  =   0 ;
114       for ( int  k  =   2 ; k  <=  K;  ++ k) {     // 从挖两个洞开始计算
115          line.init();         // 左链初始化
116          line.insert(Point(rect[ 1 ].H,  0 ));     // 预先插入决策点(h[1], dp[x][0])。
117  #if  DEBUG
118           for ( int  i  =   1 ; i  <=  N;  ++ i)
119              printf( " %15lld " , dp[pre][i]); 
120          printf( " \n " );
121  #endif
122           for ( int  i  =   1 ; i  <=  N;  ++ i) {
123              dp[cur][i]  =  line.optimize(rect[i].W);     // 计算斜率优化出来的dp值
124               if (i  <  N) {
125                  line.insert(Point(rect[i + 1 ].H, dp[pre][i]));     // 将之前计算的dp值作为可能的决策点插入
126              }
127          }
128          cur  =  (cur + 1 ) % 2 ;
129          pre  =   1   -  cur;
130           if (dp[cur][N]  ==  dp[pre][N])
131              cnt ++ ;
132           if (cnt  >   5 )
133               break ;
134      }
135      printf( " %I64d\n " , dp[pre][N]);
136  }
137 
138  int  main() {
139  #if  DEBUG
140      freopen( " test.in " " r " , stdin);
141      freopen( " test.out " " w " , stdout);
142  #endif
143       while (scanf( " %d%d " & N,  & K)  ==   2 ) {
144           for ( int  i  =   1 ; i  <=  N;  ++ i)
145              scanf( " %d%d " & rect[i].W,  & rect[i].H);
146          work();
147      }
148       return   0 ;
149  }
150 

 

 

转载于:https://www.cnblogs.com/destinydesigner/archive/2010/11/21/1883592.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值