DP的一些杂记

1. 最长公共上升序列(LCIS)

在做决策时,同时考虑2个条件. P版源程序.

 

program lcis;
const
   maxn=1000;
var
   a,b,num:array[1..maxn] of longint;
   l1,l2,n,m,t:longint;
begin
   assign(input,'in.in');
   assign(output,'out.out');
   reset(input);
   rewrite(output);
   read(n);
   for l1:=1 to n do
      read(a[l1]);
   read(m);
   for l1:=1 to m do
      read(b[l1]);
   fillchar(num,sizeof(num),0);  //表面看起来num似乎不需要初始化,但其实需要……
   for l1:=1 to n do   //每轮更新a[l1]=b[x]的所有情况
      begin
      t:=0;
      for l2:=1 to m do
         if a[l1]>b[l2] then  //只要出现小的就可以更新
            if num[l2]>t then
               t:=num[l2]
            else
         else
            if a[l1]=b[l2] then
               if t+1>num[l2] then
                  num[l2]:=t+1;
      end;
   t:=0;
   for l1:=1 to m do
      if num[l1]>t then
         t:=num[l1];
   writeln(t);
   close(input);
   close(output);
end.        

 

2. DP的斜率优化

POJ 1180

解析:

    这个题目在斜率优化上算是很经典的了的,题目大意也不难理解。

    这样吧,慢慢来。假如现在不考虑它的数据范围,如果要你裸做的话……搜索?不可能。Dp吧,方程大概可以这样想出来:

     从前往后做的话……不好搞的。所以倒推吧。用F[ i ] 表示从 i 开始到 N 已经全部做完了的最小代价。这样转移会很方便的~

     F[ i ] := Min( F[j] + (S + sumT[i] - sumT[j]) * sumF[i] )


     这样的话转移的这一段工作的代价不仅仅在这个地方计算了一次,在算前面的地方也同样计算了一次。所以就可以证明这个方程的正确性啦~

     然后考虑数据范围之后就发现根本没有办法做(废话……),这样做当然不行啦。需要优化一下,不能在状态的地方优化就在转移的地方优化吧。

     考虑转移到 i 这个地方来的时候是分别从 j1 j2
(J1<J2)转移过来的,那么这两个值什么时候有大小关系呢?来算算。假如从J1转移过来的时候更优的话:

   
( F[ j1 ] + ( S + sumT[ i ] - sumT[ j1 ] ) * sumF[ i ] ) - ( F[ j2 ] + ( S + sumT[ i ] - sumT[ j2 ] ) * sumF[ i ] ) < 0
=> F[ j1 ] - F[ j2 ] + sumF[ i ] * ( sumT[ j2 ] - sumT[ j1 ] ) < 0
=> F[ j1 ] - F[ j2 ] < sumF[ i ] * ( sumT[ j1 ] - sumT[ j2 ] )
因为 j1 < j2 所以 ( sumT[ j1 ] - sumT[ j2 ] ) > 0
=> ( F[ j1 ] - F[ j2 ] ) / ( sumT[ j1 ] - sumT[ j2 ] ) < sumF[ i ]


    那么如果有两个转移满足上面的要求那么就可以直接无视掉J2了不是么?而且还可以知道,sumF[ i ] 是递增的
,那么只要有一次无视掉了J2那么以后都不会从这里转移过来了塞^.^

    现在就好办了,就可以直接用一个队列优化来维护这个值就可以了,队列里面保存可以转移过来的位置,然后套上面的方程,判断是否可以,然后做,就可以啦~

    恩~完美的优化了一维~


程序:

Program Gp ;

Const
    maxn = 10001 ;

Var
    sumT , sumC                         : array[ 0 .. maxn ] of longint ;
    f                                   : array[ 0 .. maxn ] of longint ;
    que                                 : array[ 0 .. maxn ] of longint ;
    n , m                               : longint ;

Procedure Init ;
var
    i , a , b                           : longint ;
begin
    readln( n ) ; readln( m ) ;
    for i := 1 to n do readln( sumT[ i ] , sumC[ i ] ) ;
    sumT[ n + 1 ] := 0 ; sumC[ n + 1 ] := 0 ;
    for i := n downto 1 do sumT[ i ] := sumT[ i + 1 ] + sumT[ i ] ;
    for i := n downto 1 do sumC[ i ] := sumC[ i + 1 ] + sumC[ i ] ;
end ;

Function Sploe( j1 , j2 : longint ) : extended ;
begin
    Sploe := ( f[ j1 ] - f[ j2 ] ) / ( sumT[ j1 ] - sumT[ j2 ] ) ;
end ;

Procedure Dp ;
var
    l , r , i                           : longint ;
begin
    f[ n + 1 ] := 0 ;
    l := 1 ; r := 1 ; que[ 1 ] := n + 1 ;
    for i := n downto 1 do
    begin
        while ( r > l ) and ( Sploe( que[ l ] , que[ l + 1 ] ) <= sumC[ i ] ) do Inc( l ) ;
        f[ i ] := f[ que[ l ] ] + ( m + sumT[ i ] - sumT[ que[ l ] ] ) * sumC[ i ] ;
        while ( r > l ) and ( Sploe( que[ r - 1 ] , que[ r ] ) >= Sploe( que[ r ] , i ) ) do Dec( r ) ;
        Inc( r ) ; que[ r ] := i ;
    end ;
    writeln( f[ 1 ] ) ;
end ;

Begin
    Init ;
    Dp ;
end .

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值