zoukankan      html  css  js  c++  java
  • 动态规划dp专题练习

    貌似开坑还挺好玩的...开一个来玩玩=v=...

    正好自己dp不是很熟悉,就开个坑来练练吧...先练个50题?小目标... 好像有点多啊QAQ

    既然是开坑,之前写的都不要了!

    50/50

    1.洛谷P3399 丝绸之路 简单的线性dp

    点我看题

    因为是开坑所以题意就不讲了,自己看题吧,一些题意比较迷的会讲一下。

    这题其实还挺简单的。

    f[i,j] 表示到第 i 个城市用了 j 天所需要的最小疲劳值。

    很快dp方程就出来了。  f[i,j]=min(f[i,j-1],f[i-1,j-1]+d[i]*c[j])   第一个是选择休息第二个是选择走。

    初始化 f[i,i]=f[i-1,i-1]+a[i]*b[i]  一天都不休息就是天天走的值

     1 var n,m:longint;
     2     i,j:longint;
     3     a,b:array[0..1000]of longint;
     4     f:array[0..1000,0..1000]of longint;
     5 function min(a,b:longint):longint;
     6 begin
     7   if a<b then exit(a) else exit(b);
     8 end;
     9 begin
    10   read(n,m);
    11   for i:=1 to n do
    12   read(a[i]);
    13   for i:=1 to m do
    14   read(b[i]);
    15   for i:=1 to n do
    16   begin
    17     f[i,i]:=f[i-1,i-1]+a[i]*b[i];
    18     for j:=i+1 to m do
    19     f[i,j]:=min(f[i,j-1],f[i-1,j-1]+a[i]*b[j]);
    20   end;
    21   writeln(f[n,m]);
    22 end.
    丝绸之路

    2.洛谷P1435 回文字串 神奇姿势 (有点套路)

    点我看题

    这题我看完确实萌比了,想了还挺久的dp,却完全没有思路,回文串?不满足无后性啊,到底怎么dp...然后当然只能厚颜无耻的悄悄咪咪的去翻了题解十分努力的耗尽脑细胞的想。

    哦~原来是这样...

    这题的姿势不得不说十分巧妙。回文串,反过来一样!所以就可以把原串反一下。

    然后?求最长公共子序列。为什么求最长公共子序列,因为回文串满足反过来是一样的,所以和反过串公共的部分就当做是回文串里原本有的,然后我萌就要加上辣些没有的,这个要加的值就是答案,要答案就小,就要子序列最长。这个姿势get!

    关于最长公共子序列,设 f[i,j]为 s串中1-i

                  s1串中1-j 的最长公共子序列。

    这样 分两种情况, s[i]=s1[j] : f[i,j]=f[i-1,j-1]+1

              s[i]<>(!=)s1[j]:f[i,j]=max(f[i-1,j],f[i,j-1])

     1 var s,s1:ansistring;
     2     n:longint;
     3     f:array[0..1500,0..1500]of longint;
     4     i,j:longint;
     5 function max(a,b:longint):longint;
     6 begin
     7   if a>b then exit(a) else exit(b);
     8 end;
     9 begin
    10   readln(s);
    11   n:=length(s);
    12   for i:=1 to n do
    13   s1:=s[i]+s1;
    14   for i:=1 to n do
    15     for j:=1 to n do
    16     if s[i]=s1[j] then f[i,j]:=f[i-1,j-1]+1 else
    17       f[i,j]:=max(f[i-1,j],f[i,j-1]);
    18   writeln(n-f[n,n]);
    19 end.
    回文字串

    3.洛谷P1481 魔族密码  简单的线性dp (预处理+简单dp)

    点我看题

    这题也比较简单。

    dp[i] 表示以 i 结束的串最多能有多少。

    很快就有dp方程

       dp[i]=max(dp[j]+1)  (1≤j≤i-1)    (满足a[j] 为 a[i]的前缀)

    由于是前缀,然后题目又保证了无重复的两个串,所以要先按长度这个关键字排下序,这样可以保证 i 之后一定没有 a[i] 的前缀 因为i之后的长度都为大于等于i的,而长度等于 i 不可能是a[i](无重复的两个串)。所以拍序后保证了dp的无后性。

    这个方程去转移的话是三重的 效率是O(n*n*s)  n只有2000 s只有75 所以三重去水是可以过的。

    可以变成两重吗?当然可以,可以预处理每个字符串1-i 这个前缀的hash值,然后转移的时候直接判hash就好了。

    这样的效率是O(n*n)砍掉了 s 当s很大的情况hash是比较必要的。

     1 var
     2     n:longint;
     3     a:array[0..2050]of string;
     4     b:array[0..2050]of longint;
     5     i,j:longint;
     6     dp:array[0..2050]of longint;
     7     ans:longint;
     8 function ok(a,b:string):boolean;
     9 var i:longint;
    10 begin
    11   for i:=1 to length(b) do
    12   if a[i]<>b[i] then exit(false);
    13   exit(true);
    14 end;
    15 function max(a,b:longint):longint;
    16 begin
    17   if a>b then exit(a) else exit(b);
    18 end;
    19 procedure qs(l,r:longint);
    20 var i,j,m,t:longint;
    21     s:string;
    22 begin
    23   i:=l;
    24   j:=r;
    25   m:=b[(l+r)>>1];
    26   repeat
    27     while b[i]<m do inc(i);
    28     while b[j]>m do dec(j);
    29     if i<=j then
    30     begin
    31       t:=b[i];b[i]:=b[j];b[j]:=t;
    32       s:=a[i];a[i]:=a[j];a[j]:=s;
    33       inc(i);
    34       dec(j);
    35     end;
    36   until i>j;
    37   if l<j then qs(l,j);
    38   if i<r then qs(i,r);
    39 end;
    40 
    41 begin
    42   readln(n);
    43   for i:=1 to n do
    44   begin
    45     readln(a[i]);
    46     b[i]:=length(a[i]);
    47     dp[i]:=1;
    48   end;
    49   qs(1,n);
    50   for i:=1 to n do
    51     for j:=1 to i-1 do
    52     if ok(a[i],a[j]) then
    53       dp[i]:=max(dp[j]+1,dp[i]);
    54   for i:=1 to n do
    55   ans:=max(ans,dp[i]);
    56   writeln(ans);
    57 end.
    魔族密码 O(n*n*s)
    const base=233;
          HR=19997;
    var
        n:longint;
        a:array[0..2050]of string;
        b,id:array[0..2050]of longint;
        hash:array[0..2050,0..200]of longint;
        i,j:longint;
        dp:array[0..2050]of longint;
        ans:longint;
    function max(a,b:longint):longint;
    begin
      if a>b then exit(a) else exit(b);
    end;
    procedure qs(l,r:longint);
    var i,j,m,t:longint;
        s:string;
    begin
      i:=l;
      j:=r;
      m:=b[(l+r)>>1];
      repeat
        while b[i]<m do inc(i);
        while b[j]>m do dec(j);
        if i<=j then
        begin
          t:=b[i];b[i]:=b[j];b[j]:=t;
          t:=id[i];id[i]:=id[j];id[j]:=t;
          s:=a[i];a[i]:=a[j];a[j]:=s;
          inc(i);
          dec(j);
        end;
      until i>j;
      if l<j then qs(l,j);
      if i<r then qs(i,r);
    end;
    
    begin
      readln(n);
      for i:=1 to n do
      begin
        readln(a[i]);
        b[i]:=length(a[i]);
        for j:=1 to length(a[i]) do
        hash[i,j]:=(base*hash[i,j-1]+ord(a[i][j]))mod HR;
        dp[i]:=1;
        id[i]:=i;
      end;
      qs(1,n);
      for i:=1 to n do
        for j:=1 to i-1 do
        if hash[id[i],b[j]]=hash[id[j],b[j]] then
          dp[i]:=max(dp[j]+1,dp[i]);
      for i:=1 to n do
      ans:=max(ans,dp[i]);
      writeln(ans);
    end.
    hash O(n*n)

    因为s比较小所以只写了1hash就能水过,s大一点的时候建议改成2hash甚至3hash。

    4.洛谷P2062 分队问题 有点萌比的dp (难qaq)

    点我看题

    这题的话是我无意在洛谷看到的,发现似曾相识,猛的记起是某次srm(某个有趣的自测赛)的题。

    然后就去翻了一下,发现还真是(居然找到原题了2333)...当时这题打了贪心挂的很惨,正解dp十分短小精悍

    但是理解了很久还是不太懂(是的,我看题解了)。

    大致是用一个 f[i]  表示 1 到 i 中 选了 i 后的最优解。

    然后用一个  g[i] 表示 f[1]..f[i-1] 中的最大值。

    这样的话对于 f[i] 可以这样转移   f[i]=g[i-a[i]]+1  大致我理解成了,可以通过得到的之前的最优解的最大值来转移,保证了这个更新答案也会是一个最优解。

    至于 i-a[i] 就是在选了 i 这个元素后保证i 需要a[i]个 所以必须在 1 到 i-a[i] 区间进行选择,然后又因为用g[i]已经保存了这个最大值,所以直接用g[i-a[i]] 更新。

    然后对于 g的更新就是 g[i]=max(f[i],g[i-1]) 这个就可以保存这个最大值了。

    QAQ还是太弱了,不是很理解。

     1 var n,i:longint;
     2     f,g,a:array[0..1000050]of longint;
     3 function max(a,b:longint):longint;
     4 begin
     5   if a>b then exit(a) else exit(b);
     6 end;
     7 procedure qs(l,r:longint);
     8 var i,j,m,t:longint;
     9 begin
    10   i:=l;
    11   j:=r;
    12   m:=a[(l+r)>>1];
    13   repeat
    14     while a[i]<m do inc(i);
    15     while a[j]>m do dec(j);
    16     if i<=j then
    17     begin
    18       t:=a[i];a[i]:=a[j];a[j]:=t;
    19       inc(i);
    20       dec(j);
    21     end;
    22   until i>j;
    23   if l<j then qs(l,j);
    24   if i<r then qs(i,r);
    25 end;
    26 begin
    27   read(n);
    28   for i:=1 to n do
    29   read(a[i]);
    30   qs(1,n);
    31   for i:=1 to n do
    32   begin
    33     if i>=a[i] then f[i]:=g[i-a[i]]+1;  //注意 i-a[i] 不能小于0
    34     g[i]:=max(f[i],g[i-1]);
    35   end;
    36   writeln(f[n]);
    37 end.
    分队问题

     5.校内胡测...

    题意是 n (5000)个 坐标xi(互不相同),有两个棋子,可以初始放在这n个坐标当中。如果满足 i,j有棋子,且在 n个坐标中有 k 坐标 且满足 k-j=j-i 辣么 i 可以跳到 k位置,问最多可以跳几次。

    这题应该是n^2 效率的我写了n^2logn(貌似是n^2...QAQ)。

    f[i,j] 表示 第一个棋子在 xi 位置,第二个棋子在 xj 位置 结束,跳的步数最大值。  (xi<xj)

    f[j,k]=max(f[i,j]+1,f[j,k])    满足 xk-xj=xj-xi

     答案就是max(f[i,j])  (1≤i≤n-1, i<j≤n)

    这是一个n^3的dp显然有个优化,确定了 i,j    xk就可以算出来 而k可以二分查找。 (先排序使xi 有序)

    显然优化到了 n^2logn

    但还有一个优化,对于 i 不变 j 增大, k必然增大。 所以每次二分的区间也可以缩小。

    然后就这样被我奇奇怪怪的写法卡AC了=v=

     1 var i,j,k,x:longint;
     2     n:longint;
     3     f:array[0..5050,0..5050]of longint;
     4     a:array[0..5050]of longint;
     5     ans:longint;
     6     last:longint;
     7 procedure qs(l,r:longint);
     8 var i,j,m:longint;
     9     t:longint;
    10 begin
    11   i:=l;
    12   j:=r;
    13   m:=a[(l+r)>>1];
    14   repeat
    15     while a[i]<m do inc(i);
    16     while a[j]>m do dec(j);
    17     if i<=j then
    18     begin
    19       t:=a[i];a[i]:=a[j];a[j]:=t;
    20       inc(i);
    21       dec(j);
    22     end;
    23   until i>j;
    24   if l<j then qs(l,j);
    25   if i<r then qs(i,r);
    26 end;
    27 function max(a,b:longint):longint;
    28 begin
    29   if a>b then exit(a) else exit(b);
    30 end;
    31 function find(x:longint):longint;
    32 var l,r,m:longint;
    33 begin
    34   l:=last;
    35   r:=n;
    36   while l<r do
    37   begin
    38     m:=(l+r+1)>>1;
    39     if a[m]<=x then l:=m else r:=m-1;
    40   end;
    41   if a[l]=x then exit(l) else exit(0);
    42 end;
    43 begin
    44   read(n);
    45   for i:=1 to n do
    46   read(a[i]);
    47   qs(1,n);
    48   for i:=1 to n-1 do
    49   begin
    50     last:=i;
    51     for j:=i+1 to n do
    52     begin
    53       x:=a[j]*2-a[i];
    54       k:=find(x);
    55       if k<>0 then
    56       begin
    57         f[j,k]:=max(f[i,j]+1,f[j,k]);
    58         ans:=max(f[j,k],ans);
    59       end;
    60       last:=k;
    61     end;
    62   end;
    63   writeln(ans);
    64 end.
    校内胡测

    6.洛谷P2066 机器分配 简单线性dp+倒推姿势 (dp一般,倒推麻烦)

    点我看题

    这题也是比较简单...

    f[i,j] 表示 前 i 个公司总共用了 j 台机器的最优解。 辣答案就是 f[n,m]

    辣么方程很好写     f[i,j]=max(f[i-1,k]+a[i,j-k])  (0≤k≤j)     

    枚举一个 k 表示 前 i-1 个公司用了 k 台 则第 i 个公司就要有 j-k 台 

    至于方案的输出,可以用倒推的方法从 n,m 推回去 由于要字典序最小,所以倒推的时候就取字典序最大。

     1 var n,m:longint;
     2     i,j,k:longint;
     3     a,f:Array[0..20,0..20]of longint;
     4     now,need:longint;
     5     ans:array[0..20]of longint;
     6 function max(a,b:longint):longint;
     7 begin
     8   if a>b then exit(a) else exit(b);
     9 end;
    10 begin
    11   read(n,m);
    12   for i:=1 to n do
    13   begin
    14     for j:=1 to m do
    15     read(a[i,j]);
    16     readln;
    17   end;
    18   for i:=1 to n do
    19   for j:=1 to m do
    20   for k:=0 to j do
    21     f[i,j]:=max(f[i,j],f[i-1,k]+a[i,j-k]);
    22   writeln(f[n,m]);
    23   now:=n;
    24   repeat
    25     need:=0;
    26     for i:=0 to m do
    27     if f[now,m]=f[now-1,i]+a[now,m-i] then
    28       if need<m-i then need:=m-i;
    29     ans[now]:=need;
    30     dec(m,need);
    31     dec(now);
    32   until now=0;
    33   for i:=1 to n do
    34   writeln(i,' ',ans[i]);
    35 end.
    洛谷P2066

    7.洛谷P1977 出租车拼车 简单线性dp (基础dp)

    点我看题

    这题的话和上一题似乎一样QAQ...随便找的题居然找到两个相同的...

    和上一题一样   设 f[i,j] 表示 前 i 辆车载了 j 个oier的最优解。辣答案就是 f[k,n]

    方程一样的QAQ   f[i,j]=min(f[i-1,j-x]+x*t[i]+d)  (1≤x≤min(z[i],j))

                           f[i,j]=f[i-1,j]  (x=0)      如果没有人上车,就不需要付 D 元 

    然后初值是注意的点   f[0,j]=∞ (1≤j≤n)(第0辆车载任何乘客都是无穷大的价值,因为载不了)

     1 var n,m,d,s:longint;
     2     f:array[0..150,0..150]of longint;
     3     t,z:array[0..150]of longint;
     4     i,j,k:longint;
     5     sum:longint;
     6 function min(a,b:longint):longint;
     7 begin
     8   if a<b then exit(a) else exit(b);
     9 end;
    10 begin
    11   read(n,m,d,s);
    12   for i:=1 to m do
    13   begin
    14     read(t[i],z[i]);
    15     inc(sum,z[i]);
    16   end;
    17   if sum<n then
    18   begin
    19     writeln('impossible');
    20     exit;
    21   end;
    22   for i:=1 to n do
    23   f[0,i]:=1 << 25;
    24   for i:=1 to m do
    25     for j:=1 to n do
    26     begin
    27       f[i,j]:=maxlongint;
    28       for k:=0 to min(z[i],j) do
    29       if k=0 then f[i,j]:=f[i-1,j] else
    30          f[i,j]:=min(f[i,j],f[i-1,j-k]+k*t[i]+d);
    31     end;
    32   writeln(f[m,n]);
    33 end.
    洛谷P1977

     上面的代码似乎有点问题...

    以上dp没有问题,思考一下另一个dp

    f[i][j] 表示 前 i 辆车载了 j 个oier的最优解。辣答案就是 f[k,n]

    换一个想法,对于代价,思考那些剩下的人所停留的代价

    f[i][j]=min(f[i-1][j-x]+(n-(j-x))*(t[i]-t[i-1]))+d (1<=x<=min(z[i],j))

    f[i][j]=f[i-1][j]+(n-j)*(t[i]-t[i-1]) (x=0)

     1 var n,m,d,s:longint;
     2     f:array[0..150,0..150]of longint;
     3     t,z:array[0..150]of longint;
     4     i,j,k:longint;
     5     sum:longint;
     6 function min(a,b:longint):longint;
     7 begin
     8   if a<b then exit(a) else exit(b);
     9 end;
    10 begin
    11   read(n,m,d,s);
    12   for i:=1 to m do
    13   begin
    14     read(t[i],z[i]);
    15     inc(sum,z[i]);
    16   end;
    17   if sum<n then
    18   begin
    19     writeln('impossible');
    20     exit;
    21   end;
    22   for i:=1 to n do
    23   f[0,i]:=1 << 25;
    24   for i:=1 to m do
    25     for j:=0 to n do
    26     begin
    27       f[i,j]:=maxlongint;
    28       for k:=0 to min(z[i],j) do
    29       if k=0 then f[i,j]:=f[i-1,j]+(n-j)*(t[i]-t[i-1]) else
    30          f[i,j]:=min(f[i,j],f[i-1,j-k]+(n-j+k)*(t[i]-t[i-1])+d);
    31     end;
    32   writeln(f[m,n]);
    33 end.
    NEW

    8.洛谷P1103 书本整理 简单线性dp(基础dp)

    点我看题

    设 f[i,j] 表示 前 i 本书选 j 本书且以i 结束的最优值。 辣答案就是 min(f[i,n-k])   (n-m≤i≤n)

    方程:   f[i,j]=min(f[x,j-1])+abs(a[x].w-a[i].w)    (1≤k≤i-1)  (2≤j≤i)

                  f[i,j]=0   (j=1)  

     1 type
     2   node=record
     3        w,h:longint;
     4   end;
     5 var i,j,k:longint;
     6     a:array[0..150]of node;
     7     n,m:longint;
     8     f:array[0..150,0..150]of longint;
     9     ans:longint;
    10 procedure qs(l,r:longint);
    11 var i,j,m:longint;
    12     t:node;
    13 begin
    14   i:=l;
    15   j:=r;
    16   m:=a[(l+r)>>1].h;
    17   repeat
    18     while a[i].h<m do inc(i);
    19     while a[j].h>m do dec(j);
    20     if i<=j then
    21     begin
    22       t:=a[i];a[i]:=a[j];a[j]:=t;
    23       inc(i);
    24       dec(j);
    25     end;
    26   until i>j;
    27   if l<j then qs(l,j);
    28   if i<r then qs(i,r);
    29 end;
    30 function min(a,b:longint):longint;
    31 begin
    32   if a<b then exit(a) else exit(b);
    33 end;
    34 begin
    35   read(n,m);
    36   for i:=1 to n do
    37   read(a[i].h,a[i].w);
    38   qs(1,n);
    39   for i:=1 to n do
    40   begin
    41     f[i,1]:=0;
    42     for j:=2 to i do
    43     begin
    44       f[i,j]:=maxlongint;
    45       for k:=1 to i-1 do
    46       begin
    47         if k>=j-1 then f[i,j]:=min(f[k,j-1]+abs(a[k].w-a[i].w),f[i,j]);
    48       end;
    49     end;
    50   end;
    51   ans:=maxlongint;
    52   for i:=n-m to n do
    53   if f[i,n-m]<ans then ans:=f[i,n-m];
    54   writeln(ans);
    55 end.
    洛谷P1103

    9.洛谷P1279 字串距离 有趣dp(注意初始化)

    点我看题

    这题的话QAQ有点奇奇怪怪QAQ,想了一会大致可做。然后悄悄咪咪看了题解。

    发现题解和自己写的差不多的dp QAQ。

    设 f[i,j] 表示 A串前 i 个字符,B串前 j 个字符的最小距离。辣答案就是 f[lena,lenb]。

    方程:  f[i,j]=min(f[i-1,j]+k,f[i,j-1]+k,f[i-1,j-1]+abs(a[i]-b[j]))

    分三类考虑 ①第 i 位为空格与第 j 位  

                      ②第 j 位为空格与第 i 位

                      ③第 i 位与第 j 位  

    然后我就成功挂掉了QAQ 又悄悄咪咪打开题解  

    没考虑初始化。

    f[i,0]=f[i-1,0]+k   B串无字符,就必须用 i 个空格。

    f[0,i]=f[0,i-1]+k   同理

    QAQ初始化要多考虑。

     1 var
     2    a,b:ansistring;
     3    lena,lenb:longint;
     4    i,j:longint;
     5    k:longint;
     6    f:array[0..2050,0..2050]of int64;
     7 function min(a,b:longint):longint;
     8 begin
     9   if a<b then exit(a) else exit(b);
    10 end;
    11 begin
    12   readln(a);
    13   lena:=length(a);
    14   readln(b);
    15   lenb:=length(b);
    16   readln(k);
    17   for i:=1 to lena do
    18   f[i,0]:=f[i-1,0]+k;
    19   for i:=1 to lenb do
    20   f[0,i]:=f[0,i-1]+k;
    21   for i:=1 to lena do
    22   begin
    23     for j:=1 to lenb do
    24     begin
    25       f[i,j]:=maxlongint;
    26       f[i,j]:=min(f[i-1,j]+k,f[i,j]);
    27       f[i,j]:=min(f[i,j-1]+k,f[i,j]);
    28       f[i,j]:=min(f[i-1,j-1]+abs(ord(a[i])-ord(b[j])),f[i,j]);
    29     end;
    30   end;
    31   writeln(f[lena,lenb]);
    32 end.
    洛谷P1279

    10.洛谷P2308 添加括号 玄学区间dp+乱搞姿势 (倒推麻烦)

    点我看题

    这题也是奇奇怪怪QAQ。

    第二问就是最基本的石子归并  设 f[i,j] 表示 i 到 j 中合并后的最小值。辣答案就是 f[1,n]

    预处理一个数组 sum[i,j] 表示合并 i 到 j 后的值是多少  sum[i,j]=sum[i,j-1]+a[j] 

    f[i,j]=min(f[i,k]+f[k+1,j]+sum[i,j])  

    重点就是第一问和第三问。

    对于这两问可以一起求。

    用一个 dfs 从[1,n] 这个状态往回推每次找到一个 x 表示 当前 [l,r] 状态是用 [l,x] 和 [x+1,r] 转移而来的。

    然后对于第一问,可以这样考虑,记录一个 numL[i] 表示 i 作为 l 被递归了几次。由题目的观察可以发现,i 作为 l 被递归的次数(除 l=r=i的情况时) 就是 i 前面(与 i 相连)的左括号数。

                                              类似的,记录一个numR[i] 表示 i 作为 r 被递归了几次。还是由题目的观察可以发现,i 作为 r 被递归的次数(除 l=r=i的情况时) 就是 i 后面的(与 i 相连)右括号数。

    当然 如果numL[i]≠0 则 numR[i]=0  因为除去了 l=r=i 的情况时。

    然后在输出的时候 对于每一个数前面输出 numL[i] 个‘(’   后面输出numR[i] 个‘)’    然后在两个数之间加 ‘+’字符即可。

    对于第二问,由于是dfs将[1,n]状态进行分割,所以就是在回溯时,直接记录 sum[l,r] 就好辣。

    然后就愉快的AC了

     1 var n:longint;
     2     i,j,k:longint;
     3     f,sum:array[0..25,0..25]of longint;
     4     a,ans,numl,numr:array[0..25]of longint;
     5     orzn:array[0..25]of string;
     6     //rp++,存有 numL[i] 个'(' 或 numR[i] 个')'
     7     zn:longint;
     8     ansl,ansr:longint;
     9 function min(a,b:longint):longint;
    10 begin
    11   if a<b then exit(a) else exit(b);
    12 end;
    13 procedure dfs(l,r:longint);
    14 var k,x:longint;
    15     s1:string;
    16 begin
    17   if l=r then exit;  
    18   // l=r 的情况就先exit掉,这样不会统计到numL,numR中 
    19   inc(numl[l]);
    20   inc(numr[r]);
    21   for k:=l to r-1 do
    22   if f[l,r]=f[l,k]+f[k+1,r]+sum[l,r] then x:=k;
    23   dfs(l,x);
    24   dfs(x+1,r);
    25   inc(zn);
    26   ans[zn]:=sum[l,r];
    27 end;
    28 begin
    29   read(n);
    30   for i:=1 to n do
    31   read(a[i]);
    32   for i:=1 to n do
    33   begin
    34     f[i,i]:=0;
    35     sum[i,i]:=a[i];
    36     for j:=i+1 to n do
    37     sum[i,j]:=sum[i,j-1]+a[j];
    38   end;
    39   for i:=n downto 1 do
    40     for j:=i+1 to n do
    41     begin
    42       f[i,j]:=maxlongint;
    43       for k:=i to j-1 do
    44       f[i,j]:=min(f[i,k]+f[k+1,j]+sum[i,j],f[i,j]);
    45     end;
    46   dfs(1,n);
    47   for i:=1 to n do
    48     if numl[i]<>0 then
    49     begin
    50       for j:=1 to numl[i] do
    51       orzn[i]:=orzn[i]+'(';
    52     end else
    53     begin
    54       for j:=1 to numr[i] do
    55       orzn[i]:=orzn[i]+')';
    56     end;
    57   for i:=1 to n do
    58   if numl[i]<>0 then write(orzn[i],a[i],'+') else
    59   begin
    60     if i=n then writeln(a[i],orzn[i]) else write(a[i],orzn[i],'+');
    61   end;
    62   writeln(f[1,n]);
    63   for i:=1 to zn do
    64   write(ans[i],' ');
    65   writeln;
    66 end.
    洛谷P2308

    11.洛谷P1140 相似基因  和第9题一样QAQ 

    点我看题

    设 f[i,j] 表示 A串前 i 个字符,B串前 j 个字符的最大相似度。辣答案就是 f[lena,lenb]。

    方程:  f[i,j]=max(f[i-1,j]+cost('-',a[i]),f[i,j-1]+cost('-',b[j]),f[i-1,j-1]+cost(a[i],b[j]))

    分三类考虑 ①第 i 位为‘-’与第 j 位  

                      ②第 j 位为‘-’与第 i 位

                      ③第 i 位与第 j 位  

    初始化一样的...基本就是等于第9题辣个字串距离。

    就是距离打个表就好了...

     1 const
     2    c:array['1'..'5','1'..'5']of longint=((5,-1,-2,-1,-3),
     3                                          (-1,5,-3,-2,-4),
     4                                          (-2,-3,5,-2,-2),
     5                                          (-1,-2,-2,5,-1),
     6                                          (-3,-4,-2,-1,0));
     7 var n,m:longint;
     8     a,b:string;
     9     i,j:longint;
    10     f:array[0..150,0..150]of longint;
    11 function max(a,b:longint):longint;
    12 begin
    13   if a>b then exit(a) else exit(b);
    14 end;
    15 begin
    16   read(n);
    17   readln(a);
    18   read(m);
    19   readln(b);
    20   delete(a,1,1);
    21   delete(b,1,1);
    22   for i:=1 to n do
    23   begin
    24     if a[i]='A' then a[i]:='1' else
    25     if a[i]='C' then a[i]:='2' else
    26     if a[i]='G' then a[i]:='3' else
    27     if a[i]='T' then a[i]:='4';
    28     f[i,0]:=f[i-1,0]+c[a[i],'5'];
    29   end;
    30   for i:=1 to m do
    31   begin
    32     if b[i]='A' then b[i]:='1' else
    33     if b[i]='C' then b[i]:='2' else
    34     if b[i]='G' then b[i]:='3' else
    35     if b[i]='T' then b[i]:='4';
    36     f[0,i]:=f[0,i-1]+c[b[i],'5'];
    37   end;
    38   for i:=1 to n do
    39   for j:=1 to m do
    40   begin
    41     f[i,j]:=-maxlongint;
    42     f[i,j]:=max(f[i-1,j]+c['5',a[i]],f[i,j]);
    43     f[i,j]:=max(f[i,j-1]+c['5',b[j]],f[i,j]);
    44     f[i,j]:=max(f[i-1,j-1]+c[a[i],b[j]],f[i,j]);
    45   end;
    46   writeln(f[n,m]);
    47 end.
    洛谷P1140

    12.codevs 1184 瓷砖覆盖  状压dp入门 (入门状压)

    点我看题

    QAQ最近既然开了dp坑,就顺便把状压一起学了吧唔...

    看了很久题解博客大概知道一点状压了吧QAQ

    设 f[i,j] 表示 铺到第i层 前 i-1层都已经铺满,且 当前层状态为 j 的方案数。

    状态指: 如果这一层中的格子铺了,辣么为1 否则为0。然后这样的一个01串转为十进制后的数。

    对于 f[i,j]<>0 的情况(有这样的状态再去转移下一层的): 可以去转移到 i+1 层,又为了保证 前 i-1 层都已经铺满这样的条件。所以 前 i+1-1 层 都要铺满。

    辣么对于第 i 层的所以可行 j 状态(f[i,j]<>0) dfs的去找转移到下一层时,下一层的状态。

    然后  f[i+1,news]+=f[i,j] 进行转移。

     1 const HR=100003;
     2 var f:array[0..50001,0..100]of int64;
     3     n,m:longint;
     4     i,j:longint;
     5 procedure dfs(x:longint;nows,news:int64);
     6 // 铺到第 x 个格子。当前行状态为nows 下一行状态为news
     7 begin
     8   if x=m+1 then  //第 i 行铺满了就进行转移了。
     9   begin
    10     inc(f[i+1,news],f[i,nows]);
    11     if f[i+1,news]>=HR then f[i+1,news]:=f[i+1,news] mod HR;
    12     //膜神犇rp++ 常数--
    13     exit;
    14   end;
    15   // 1 shl (x-1) and nows 是判断 nows 的第 x 格是否是1 如果是1则>0
    16   // 对于 x 格是1的情况不需要铺
    17   if (1 shl (x-1)) and nows>0 then dfs(x+1,nows,news) else
    18   begin
    19     if ((1 shl x) and nows=0)and(x+1<=m) then dfs(x+2,nows,news);
    20     // 对于 x+1 格也是0 时,可以铺横的。对下一行无影响
    21     dfs(x+1,nows,news or (1 shl (x-1)));
    22     // 铺竖的,对下一行的影响就是 把 x 这个位置变成 1
    23     // news or (1 shl (x-1)) 就是 对news 的第 x 格置1的操作   
    24   end;
    25 end;
    26 begin
    27   readln(m,n); 
    28   //读入m,n 而不读入n,m是因为m过于大 1 <<m会爆掉
    29   //而且2^m效率是会挂的。    
    30   f[1,0]:=1;  //第 1 层什么都不铺的方案为1
    31   for i:=1 to n do  //枚举铺的每一层
    32   for j:=0 to (1 shl m) -1 do //枚举 i 层的所有状态
    33   if f[i,j]<>0 then dfs(1,j,0); //如果是可行状态就对下一层进行转移
    34   writeln(f[n+1,0]);
    35 end.
    codevs1184

    对于 f 数组 还可以滚动数组优化。我懒得写了=v=

    13.洛谷P1879 [USACO06NOV]玉米田Corn Fields  状压dp入门题

    点我看题

    唔...这题的话也是一道比较简单的状压dp...随便乱想的,居然一次过啦=v=  (虽然瞄了一眼题解,但是没细看)

    设 f[i,j] 表示 第 i 行 且 第 i 行的状态为 j 时的方案数。 辣么答案就是 sum(f[n,j])  j 是 对于第 n 行来说可行的状态

    辣么方程应该是   f[i,j]=sum(f[i-1,k])  只要枚举一个 k 状态表示上一行的状态,再判这个状态是否合法,就行辣!=v=

    对于一个状态 x ,合法就要满足不存在任意两个相邻格子都为1且 不存在一个格子为1但原地图为0 。可以O(m) 判一下。

    对于两个状态 x (当前行)和 y(上一行) ,合法就要满足不存在 任意两个同列格子都为1。一样O(m)判一下。

    这样的话理论效率为 O(n*2m*2m*m)   看起来似乎是卡着过的,但是实际上的时间却很快,原因应该是一个小剪枝。

    如果对于当前行的 j 状态已经不合法 就没必要再去枚举 k。 而合法的 j 其实不多,所以实际上效率是很快哒=v=

     1 const HR=100000000;
     2 
     3 var n,m:longint;
     4     i,j,k:longint;
     5     ans:longint;
     6     f:array[0..15,0..35000]of longint;
     7     a:array[0..15,0..15]of longint;
     8 function isnot(i,x:longint):boolean;
     9 begin
    10   exit(1 << (i-1) and x>0);
    11 end;
    12 function ok(c,x:longint):boolean;
    13 var i:longint;
    14 begin
    15   for i:=1 to m do
    16   begin
    17     if (isnot(i,x))and(a[c,i]=0) then exit(false);
    18     if (i>1)and(isnot(i,x))and((1 << (i-2)) and x>0) then exit(false);
    19   end;
    20   exit(true);
    21 end;
    22 function check(x,y:longint):boolean;
    23 var i:longint;
    24 begin
    25   for i:=1 to m do
    26   if isnot(i,x)and isnot(i,y) then exit(false);
    27   exit(true);
    28 end;
    29 begin
    30   read(n,m);
    31   for i:=1 to n do
    32   begin
    33     for j:=1 to m do
    34     read(a[i,j]);
    35     readln;
    36   end;
    37   f[0,0]:=1;
    38   for i:=1 to n do
    39   for j:=0 to (1 << m)-1 do
    40   if ok(i,j) then
    41   begin
    42     for k:=0 to (1 << m)-1 do
    43     if ok(i-1,k) and check(j,k) then
    44     begin
    45       inc(f[i,j],f[i-1,k]);
    46       if f[i,j]>=HR then f[i,j]:=f[i,j] mod HR;
    47     end;
    48     if i=n then
    49     begin
    50       inc(ans,f[i,j]);
    51       if ans>=HR then ans:=ans mod HR;
    52     end;
    53   end;
    54   writeln(ans);
    55 end.
    洛谷1879

    大致都已经很明白了就不注释了QAQ 懒兔纸一只

    14.bzoj1087(codevs2451) 互不侵犯King  状压dp

    这题写了个题解,毕竟是bzoj的可以水博客

    点我看题解

    15.bzoj1879 [SDOI2009]Bill的挑战   状压dp

    估计是bzoj的题都会单独写一篇。

    点我看题解

    15题辣!=v=

    还有35题QAQ好漫长...

    16.洛谷P1336 最佳课题选择   (一般)

    点我看题

    这题的话,也不会很难...

    设  f[i,j] 表示用前 i 中课题共写了 j 篇论文的最优解 答案就是 f [m,n]

    初始化为 f [0,i]=∞ (1≤i≤n)  不用课题就想完成论文就是无穷大

    f[0,0]=0  不用课题,不完成论文的最优解就是 0

    方程  f[i,j]=min(f[i-1,j-x]+a[i]*xb[i]) (枚举 x 表示 第 i 个课题写x篇)

    幂我用快速幂算了...暴力应该也是可以的

    注意一下long long

     1 var n,m:longint;
     2     a,b:array[0..205]of longint;
     3     f:array[0..205,0..205]of int64;
     4     i,j,x:longint;
     5 function min(a,b:int64):int64;
     6 begin
     7   if a<b then exit(a) else exit(b);
     8 end;
     9 function ksm(a,b:int64):int64;
    10 var t,y:int64;
    11 begin
    12   t:=1;
    13   y:=a;
    14   while b<>0 do
    15   begin
    16     if b and 1=1 then t:=t*y;
    17     y:=y*y;
    18     b:=b shr 1;
    19   end;
    20   exit(t);
    21 end;
    22 begin
    23   read(n,m);
    24   for i:=1 to m do
    25   read(a[i],b[i]);
    26   for i:=1 to n do
    27   f[0,i]:=maxlongint;
    28   for i:=1 to m do
    29     for j:=1 to n do
    30     begin
    31       f[i,j]:=maxlongint;
    32       for x:=0 to j do
    33       f[i,j]:=min(f[i,j],f[i-1,j-x]+a[i]*ksm(x,b[i]));
    34     end;
    35   writeln(f[m,n]);
    36 end.
    luogu P1336

    17.洛谷P2029 跳舞 乱写dp (一般)

    点我看题

    f[i,j] 表示 到第 i 个 前面已经踩了 j 个 的最优解 答案就是 max(f[n,i]) (1≤i≤n)

    方程 f[i,j]=max(f[i-1,j]-s[i],f[i-1,j-1]+s[i]+b[i])  (j mod t=0的情况,可以选择不踩,就是 -s[i] 还有就是踩 +s[i]+b[i])

           同样 f[i,j]=max(f[i-,j]-s[i],f[i-1,j-1]+s[i])    (j mod t≠0的情况balalala)

     初始化为  f[i,0]=f[i-1,0]-s[i]  一个都不踩就都要扣掉

     f[0,0]=0  

     1 var i,j:longint;
     2     s,b:array[0..5050]of longint;
     3     f:array[0..5050,0..5050]of longint;
     4     n,t,ans:longint;
     5 function max(a,b:longint):longint;
     6 begin
     7   if a>b then exit(a) else exit(b);
     8 end;
     9 begin
    10   read(n,t);
    11   for i:=1 to n do
    12   read(s[i]);
    13   for i:=1 to n do
    14   read(b[i]);
    15   for i:=1 to n do
    16   begin
    17     f[i,0]:=f[i-1,0]-s[i];
    18     for j:=1 to i do
    19     if j mod t=0 then f[i,j]:=max(f[i-1,j]-s[i],f[i-1,j-1]+s[i]+b[i]) else
    20                       f[i,j]:=max(f[i-1,j]-s[i],f[i-1,j-1]+s[i]);
    21   end;
    22   for i:=1 to n do
    23   ans:=max(ans,f[n,i]);
    24   writeln(ans);
    25 end.
    luoguP2029

    18.洛谷P2704 炮兵阵地   状压dp

    点我看题

    这个题很像第14题,就是加强了一下,改了个方向和地形的限制而已

    因为关系到了两行,所以把方程改成这样

    f[i,j,x] 表示 前 i 行 第i行为 j 状态 第 i-1 行为 x 状态的最优解

    然后枚举一个 y 表示 第i-2 行的状态,然后暴力判 x,y,j是否合法

    如果合法就转移   f[i,j,x]=max(f[i,x,y]+j状态中的1个数)

    显然O(n*2m*2m*2m)是会T的不要不要的

    想想优化?当然是想到剪枝预处理。

    预处理 num[i,j] 表示 第 i 行为 j 状态是否合法,如果合法就为 j状态中 1 的个数,如果不合法,就为-1  

    这样会剪掉很多很多的枚举范围,但还是会T (懒兔纸懒得再写优化所以交完T后不得不去改)

    还有一个优化就是 要判 任意两行拼起来后是否合法,这个也会浪费很多时间,所以预处理

    can[i,j] 表示 i 状态和 j 状态拼起来后是否合法 若合法就 true 不合法就是 false

    这样就又减掉了重复的判断时间,然后就愉快的AC辣

     1 var n,m:longint;
     2     x,y,i,j:longint;
     3     num:array[-1..105,0..1200]of longint;
     4     can:array[0..1200,0..1200]of boolean;
     5     f:array[-1..105,0..1200,0..1200]of longint;
     6     ans:longint;
     7     a:array[0..105,0..15]of char;
     8 function isnot(x,i:longint):boolean;
     9 begin
    10   exit(x and (1 << (i-1))>0);
    11 end;
    12 function ok(thei,x:longint):longint;
    13 var i,s:longint;
    14 begin
    15   s:=0;
    16   for i:=1 to m do
    17   begin
    18     if isnot(x,i) then inc(s);
    19     if (a[thei,i]='H')and(isnot(x,i)) then exit(-1);
    20     if (i>=2)and(isnot(x,i)and isnot(x,i-1)) then exit(-1);
    21     if (i>=3)and(isnot(x,i)and isnot(x,i-2)) then exit(-1);
    22   end;
    23   exit(s);
    24 end;
    25 function check(a,b:longint):boolean;
    26 var i:longint;
    27 begin
    28   for i:=1 to m do
    29   if isnot(a,i)and isnot(b,i) then exit(false);
    30   exit(true);
    31 end;
    32 function max(a,b:longint):longint;
    33 begin
    34   if a>b then exit(a) else exit(b);
    35 end;
    36 begin
    37   readln(n,m);
    38   for i:=1 to n do
    39   begin
    40     for j:=1 to m do
    41     read(a[i,j]);
    42     readln;
    43   end;
    44   for i:=1 to n do
    45   for j:=0 to (1 << m)-1 do
    46   num[i,j]:=ok(i,j);
    47   for j:=1 to (1 << m)-1 do
    48   begin
    49     num[0,j]:=-1;
    50     num[-1,j]:=-1;
    51   end;
    52   for i:=0 to (1 << m)-1 do
    53   for j:=0 to (1 << m)-1 do
    54   can[i,j]:=check(i,j);
    55   for i:=1 to n do
    56     for j:=0 to (1 << m)-1 do
    57     if num[i,j]>=0 then
    58     begin
    59       for x:=0 to (1 << m)-1 do
    60       if (num[i-1,x]>=0)and(can[j,x]) then
    61       begin
    62         for y:=0 to (1 << m)-1 do
    63         if (num[i-2,y]>=0)and(can[j,y])and(can[x,y]) then
    64           f[i,j,x]:=max(f[i-1,x,y]+num[i,j],f[i,j,x]);
    65         if i=n then ans:=max(f[i,j,x],ans);
    66       end;
    67     end;
    68   writeln(ans);
    69 end.
    洛谷P2704 炮兵阵地

    19.洛谷P2915 [USACO08NOV]奶牛混合起来Mixed Up Cows   状压dp

    点我看题

    嗯...设 f[i,j] 表示 状态为 j 时 放在最后的牛是 i 的方案数 辣么答案就是 sum(f[i,1 << n-1]) (1≤i≤n)

    这样的话方程就是

    f[i,j or (1 << (i-1))]+=f[k,j]

    这样的话会发现 i 和 k 的先后求的顺序是不同的,所以要改一下枚举的顺序 先枚举 j 再枚举 i,k

    嗯...比较简单吧

     1 var n,m:longint;
     2     a:array[0..20]of longint;
     3     f:array[0..20,0..70000]of int64;
     4     i,j,k:longint;
     5     ans:int64;
     6 function ok(thei,x:longint):boolean;
     7 var i:longint;
     8 begin
     9   exit(x and (1 << (thei-1))=0);
    10 end;
    11 begin
    12   read(n,m);
    13   for i:=1 to n do
    14   read(a[i]);
    15   for i:=1 to n do
    16   f[i,(1 << (i-1))]:=1;
    17   for j:=0 to (1 << n)-1 do
    18   for i:=1 to n do
    19   if ok(i,j) then
    20   begin
    21     for k:=1 to n do
    22     if not ok(k,j) then
    23       if abs(a[k]-a[i])>m then inc(f[i,j or (1 << (i-1))],f[k,j]);
    24   end;
    25   for i:=1 to n do
    26   inc(ans,f[i,(1 << n)-1]);
    27   writeln(ans);
    28 end.
    洛谷P2915

    20.P1273 有线电视网  树形dp (较难)

    点我看题

    嗯...又是看了题解的QAQ

    dp[i,j] 表示 以 i 为根的子树中共选了 j 个叶子节点后剩下的最大值。

    辣么答案就是 max(i) (1≤i≤n 且 dp[1,i]≥0)

    dp[x,j]=max(dp[x,j],dp[x,j-k]+dp[now,k]-e[i].z)    now为x的儿子,这个方程就是 枚举k 表示now这个儿子的子树中选了k个子节点。

    由于每一个叶子节点是只能被选到一次的,和01背包相似,所以 j 要倒着枚举。

    初值 所有的叶子节点x 的 dp[x,1] 为用户有的钱。

    这样是一个 n^3 的dp,会TLE (50分)   想想优化。

    对于 x 节点,实际上 j 不需要枚举到m这么大,只要枚举到sum[x] (sum[x] 表示 以x为根的子树中叶子节点的个数)

    这样可以优化很多无效枚举,然后就100辣。

     1 type
     2   node=record
     3       y,z:longint;
     4       next:longint;
     5   end;
     6 var n,m:longint;
     7     x,z,k,ans:longint;
     8     i,j:longint;
     9     dp:Array[0..3020,0..3020]of longint;
    10     e:array[0..3020]of node;
    11     tot:longint;
    12     first,sum:array[0..3020]of longint;
    13 function max(a,b:longint):longint;
    14 begin
    15   if a>b then exit(a) else exit(b);
    16 end;
    17 function min(a,b:longint):longint;
    18 begin
    19   if a<b then exit(a) else exit(b);
    20 end;
    21 procedure adde(x,y,z:longint);
    22 begin
    23   e[tot].next:=first[x];
    24   e[tot].y:=y;
    25   e[tot].z:=z;
    26   first[x]:=tot;
    27   inc(tot);
    28 end;
    29 procedure dfs(x:longint);
    30 var i,j,k,now:longint;
    31 begin
    32   i:=first[x];
    33   while i<>-1 do
    34   begin
    35     now:=e[i].y;
    36     dfs(now);
    37     i:=e[i].next;
    38     inc(sum[x],sum[now]);
    39   end;
    40   i:=first[x];
    41   while i<>-1 do
    42   begin
    43     now:=e[i].y;
    44     for j:=sum[x] downto 1 do
    45       for k:=1 to min(sum[now],j) do
    46       dp[x,j]:=max(dp[x,j],dp[x,j-k]+dp[now,k]-e[i].z);
    47     i:=e[i].next;
    48   end;
    49 end;
    50 begin
    51   read(n,m);
    52   for i:=1 to n do
    53   first[i]:=-1;
    54   for i:=1 to n-m do
    55   begin
    56     read(k);
    57     for j:=1 to k do
    58     begin
    59       read(x,z);
    60       adde(i,x,z);
    61     end;
    62   end;
    63   for i:=1 to n do
    64   for j:=1 to m do
    65   dp[i,j]:=-1 << 25;
    66   for i:=n-m+1 to n do
    67   begin
    68     read(dp[i,1]);
    69     sum[i]:=1;
    70   end;
    71   dfs(1);
    72   for i:=1 to m do
    73   if dp[1,i]>=0 then ans:=i;
    74   writeln(ans);
    75 end.
    洛谷P1273

    惨啊QAQ都一个多月了,还没到一半耶QAQ

    由于noip,原本打算noip前就填好的,结果dp的一些好题不是很好找(主要是自己不会QAQ再加上初三老年菜兔时间比较少了)

    所以捏,估计这个坑先空一会,先转战noip,然后如果有刷到就加一点进来,所以不再局限于只刷dp题了。


    啊哈~我回来啦!每次登博客看到这个顶置,不存在的良心都会很疼qwq 弃太久了不行啊...还是得回来补掉,不然老年菜兔的实力实在太菜了...

    唔...不过这个坑是真的难填qwq...所以估计很简单的dp也放进来吧...随便凑个数了...因为最近都在补cf 所以题估计会放大部分cf的...cf的dp题遇到的不多...

    所以填坑的道路十分漫长qwq  (突然发现是有分割线这种东西的...我之前都手动分割线...似乎显得很傻..._(:з」∠)_)

    坑的格式改一下吧...点我看题这样的东西好像很奇奇怪怪,直接搞题目上不就好了qwq 蠢到极点的兔纸...

    21.cf910A   入门dp (最简单)

    直接二重对可以到达的地方更新一下就好啦

    dp[j]=min(dp[i]+1,dp[j])   (s[j]="1")

    初始值就是dp[1]=0   dp[i]=∞  (2≤i≤n)

     1 var n,d:longint;
     2     i,j:longint;
     3     s:string;
     4     dp:array[0..500]of longint;
     5 function min(a,b:longint):longint;
     6 begin
     7   if a<b then exit(a) else exit(b);
     8 end;
     9 begin
    10   readln(n,d);
    11   readln(s);
    12   for i:=2 to n do
    13   dp[i]:=1 << 25;
    14   dp[1]:=0;
    15   for i:=1 to n-1 do
    16     for j:=i+1 to min(i+d,n) do
    17     if s[j]='1' then dp[j]:=min(dp[i]+1,dp[j]);
    18   if dp[n]=1 << 25 then dp[n]:=-1;
    19   writeln(dp[n]);
    20 end.
    cf910A

    22.cf909C 比较灵活的dp+前缀和优化 (难)

    这题完全不懂怎么写dp...最后看题解了...理解了一番,大致懂了...

    dp[i,j]表示到第 i 行代码,这行代码用了 j 个缩进

    可以发现 j 最多为 n-1

    初始 : dp[1,0]=1;

    第一行代码必须是缩进为0的 方案为 1 

    缩进的意思为: 如上图   ①代码的缩进是 0

                  ②代码的缩进是 1

                  ③代码的缩进是 1

                  ④代码的缩进是 2

    那答案就为 sum(dp[n,i])  (0≤i≤ n-1)

    考虑转移   如果第 i 条代码是 “f”  那下一条代码必须缩进 所以   dp[i+1,j]=dp[i,j-1]

           如果第 i 条代码是 “s” 那下一条代码可以是 ≤i 缩进的 任意缩进

           这个情况下 我萌设 i+1的缩进为 j    i 的缩进为 k  则满足(0≤j≤k≤n-1) 所以 dp[i+1,j]+=dp[i,k]

       整理得:  dp[i+1,j]=dp[i,j-1]   (s[i]="f")

          dp[i+1,j]+=dp[i,k]   (s[i]="s")

    转移的时候枚举 i 和 j 

    对于 k (k≥j)  若枚举 就要 O(N3会超时。

    发现 dp[i,k]是可以前缀和先预处理的 (当然后缀和也可以,而且更方便,但是我写的时候写了前缀和啊啊啊啊qwq,就懒的改代码了)

    这样就优化到 O(N2) 于是不愉快的AC了

    哇的一声哭出来QAQ dp...好...好难啊QAQ

     1 Const HR=1000000007;
     2 var n,ans,num:int64;
     3     i,j,k:longint;
     4     c:array[0..5500]of char;
     5     dp:array[0..5500,0..5500]of int64;
     6     sum:array[0..5500]of int64;
     7 begin
     8   readln(n);
     9   for i:=1 to n do
    10   readln(c[i]);
    11   dp[1,0]:=1;
    12   for i:=1 to n do
    13   begin
    14     sum[0]:=dp[i,0];
    15     for j:=1 to n-1 do
    16     sum[j]:=(sum[j-1]+dp[i,j])mod HR;
    17     for j:=0 to n-1 do
    18     begin
    19       if c[i]='f' then
    20       begin
    21         if j>0 then dp[i+1,j]:=dp[i,j-1];
    22       end else
    23       begin
    24         num:=sum[n-1];
    25         if j>0 then num:=(num+HR-sum[j-1]) mod HR; 
    26         //注意因为我是前缀和,需要减,所以mod就要改成 +HR 后再mod 
    27         inc(dp[i+1,j],num);
    28         if dp[i+1,j]>=HR then dp[i+1,j]:=dp[i+1,j] mod HR;
    29       end;
    30     end;
    31   end;
    32   writeln(sum[n-1]);
    33 end. 
    cf909C

    23.洛谷 P1164 小A点菜 入门背包dp (入门背包)

    f[i,j] 表示前 i 个菜花 j 元的方案数

    则答案为 f[n,m]

    初值 f[0,0]=1

    对于每一个菜能选或者不选

    所以 f[i,j]=f[i-1,j-a[i]]+f[i-1,j] (前为选的方案,后为不选的方案)

    这样已经可以AC,考虑空间优化,由于 方程都是只和 i-1 有关的,所以考虑滚动数组,背包那样优化。

    则 设 f[j] 表示花j元的方案数

    f[j]+=f[j-a[i]]

    注意是01背包所以 j 是倒着的

     1 var n,m:longint;
     2     i,j:longint;
     3     f:array[0..150,0..10500]of longint;
     4     a:array[0..150]of longint;
     5 begin
     6   read(n,m);
     7   for i:=1 to n do
     8   read(a[i]);
     9   f[0,0]:=1;
    10   for i:=1 to n do
    11   for j:=0 to m do
    12   begin
    13     if j>=a[i] then f[i,j]:=f[i,j]+f[i-1,j-a[i]];
    14     f[i,j]:=f[i,j]+f[i-1,j];
    15   end;
    16   writeln(f[n,m]);
    17 end.
    小A点菜 二维
     1 var n,m:longint;
     2     i,j:longint;
     3     f:array[0..10500]of longint;
     4     a:array[0..150]of longint;
     5 begin
     6   read(n,m);
     7   for i:=1 to n do
     8   read(a[i]);
     9   f[0]:=1;
    10   for i:=1 to n do
    11   for j:=m downto a[i] do
    12   if j>=a[i] then f[j]:=f[j]+f[j-a[i]];
    13   writeln(f[m]);
    14 end.
    小A点菜 一维

     24.洛谷 P1064 金明的预算方案 改良背包 (套路)

    这个dp...加了一个附加条件qwq...于是我开始萌比了...悄悄咪咪看了一眼题解瞬间就会了...

    因为附件最多只有2个,所以先预处理每一个主件的附件是什么... son[i,1]表示 i 的第一个附件 son[i,2]表示 i 的第二个附件

    然后分5类dp主件 

    ①不选这个主件。

    ②只选这个主件。

    ③选这个主件外加第一个附件。

    ④选这个主件外加第二个附件。

    ⑤选这个主件外加第一个附件和第二个附件。

    然后分别dp一下就好了...

    方程很长qwq...和普通背包的 f 是一样的含义

    f[j]=max(f[j],f[j-a[i]]+a[i]*b[i]         ①和②

         ,f[j-a[i]-son[i,1]]+a[i]*b[i]+a[son[i,1]]*b[son[i,1]]    ③

           ,f[j-a[i]-son[i,2]]+a[i]*b[i]+a[son[i,2]]*b[son[i,2]]     ④

           ,f[j-a[i]-son[i,1]-son[i,2]]+a[i]*b[i]+a[son[i,1]]*b[son[i,1]]+a[son[i,2]]*b[son[i,2]])   ⑤

    答案 f[n]

     1 var i,j,k:longint;
     2     cost:int64;
     3     f:array[0..35000]of int64;
     4     son:Array[0..100,0..5]of longint;
     5     a,b,c:array[0..100]of longint;
     6     n,m:longint;
     7 function max(a,b:int64):int64;
     8 begin
     9   if a>b then exit(a) else exit(b);
    10 end;
    11 begin
    12   read(n,m);
    13   for i:=1 to m do
    14   begin
    15     read(a[i],b[i],c[i]);
    16     if c[i]>0 then
    17     begin
    18       inc(son[c[i],0]);
    19       son[c[i],son[c[i],0]]:=i;
    20     end;
    21   end;
    22   for i:=1 to m do
    23   if c[i]=0 then
    24   begin
    25     for j:=n downto a[i] do
    26     begin
    27       cost:=a[i]*b[i];
    28       f[j]:=max(f[j],f[j-a[i]]+cost);
    29       for k:=1 to son[i,0] do
    30       if j-a[i]>=a[son[i,k]] then
    31         f[j]:=max(f[j],f[j-a[son[i,k]]-a[i]]+cost+a[son[i,k]]*b[son[i,k]]);
    32       cost:=cost+a[son[i,1]]*b[son[i,1]]+a[son[i,2]]*b[son[i,2]];
    33       if j-a[i]>=a[son[i,1]]+a[son[i,2]] then
    34         f[j]:=max(f[j],f[j-a[i]-a[son[i,1]]-a[son[i,2]]]+cost);
    35     end;
    36   end;
    37   writeln(f[n]);
    38 end.
    P1064 金明的预算方案

    懒的筛好题了...只要稍微有些难度的就放进来,不然坑补不完 QAQ...

    25.P1049 装箱问题 入门背包dp (入门背包)

    noip2001T4这么简单的咩qwq

    这题做法很多,我想的是设 f[j] 表示前 i 件物品拼出 j 是否可行,可行则为 true 不可行为 false

    答案就是倒着枚举找到一个 f[j]=true  答案为 v-j

    如果 f[j-a[i]]=true 则 f[ j ]=f[j-a[i]]

     1 var
     2    f:array[0..25000]of boolean;
     3    i,j:longint;
     4    x,v,n:longint;
     5 
     6 begin
     7   read(v);
     8   read(n);
     9   f[0]:=true;
    10   for i:=1 to n do
    11   begin
    12     read(x);
    13     for j:=v downto x do
    14     if f[j-x] then f[j]:=true;
    15   end;
    16   for i:=v downto 0 do
    17   if f[i] then
    18   begin
    19     writeln(v-i);
    20     exit;
    21   end;
    22 end.
    装箱问题

    也可以类似 第23题一样做,如果最后的 f[j]>0 就相当于 f[j]=true 了,但是bool 省空间...所以我打的是bool

    还可以将价值看成体积来做一个01背包...

    很多做法,做法比较多所以随便也拉进来坑里吧...

    26.cf914C 不资道什么dp...可能是数位? (难+套路)

    这题的话qwq...想了很久没想法,悄悄咪咪翻了官方题解,没看懂,然后百度,没看懂...只资道是dp...大概看了一些思路,然后就又滚去想了...

    我这里将题目中的 n 改为 s。

    由于 s 十分大, 有 21000,但是思考一下,发现s只要经过一次操作,就能变得小于1000,因为s在二进制下最多有1000位,假设都为1,则操作后变为1000。

    为了方便,可以不直接使用1000,我萌设 n=length(s) 即 s 在二进制下的位数。

    而对于 1~1000 中的数,我萌是可以直接暴力判需要多少步变为 1 的。

    这里我采用类似dp的一个递推来求。

    num[i] 表示 i 这个数变为 1 需要的次数。

    num[1]=0

    num[i]=num[y]+1 (其中,2≤i≤n,y为 i 在二进制下1的个数)

    这个用 O(n log n) 可以处理出来。

    这时我萌反向思考一下,将问题变为 对于一个数 x,有多少个 y 满足y在二进制下的 1 有 x 个且 y≤n。

    而对于原问题,答案即 满足 num[x]=k-1 的 x 能找到的 y 的个数之和。

     好的,接下来是真正的dp,解决 找有多少个y的dp。

    dp[i,j,0] 表示 1~i 中选 j 个 1,且 t[x]=s[x](1≤x≤i,其中 t 表示选出的 y 的二进制,如s=“110”,那dp[3,3,0]=0{能构造出的t=“111”,无法满足与s相同},dp[3,2,0]=1{能构造出的t=“110” t=“101” t=“011”,满足s=t的有1个,为t=“110”}) 

    初值 dp[0,0,0]=1。

    设 dp[i,j,1] 表示1~i 中选 j 个1,且存在 x 使 t[x]≠s[x]{具体来说,t[x]=“0” s[x]=“1”} (1≤x≤i,其中 t 表示选出的 y 的二进制,与上面类似,dp[3,3,1]=1,dp[3,2,1]=2)

    初值 dp[i,0,1]=1。

    转移:

    对于 s[i]=“1” 时 :dp[i,j,0]=dp[i-1,j-1,0] {要保证和s[i]取的相同,也就是取1}

               dp[i,j,1]=dp[i-1,j,0]{在 i 位置取0使原来的s=t变为s≠t}+dp[i-1,j-1,1]{由于原来就是不同的,说明后面的既可以取1也可以取0,这里为取1的方案}+dp[i-1,j,1]{与上类似,这里是取0的方案}

    对于 s[i]=“0” 时 :dp[i,j,0]=dp[i-1,j,0] {要保证和s[i]取的相同,也就是取0}

             dp[i,j,1]=dp[i-1,j-1,1]{由于原来就是不同的,说明后面的既可以取1也可以取0,这里为取1的方案}+dp[i-1,j,1]{与上类似,这里是取0的方案}   

    (注意:不加dp[i-1,j,0]的原因是如果加了就会和s相同,而如果加 dp[i-1,j-1,0]就会大于n,这不是正确的答案。)

    对于一个 x 的答案为dp[n,x,1]{y不和s相同且小于s}+dp[n,x,0]{y和s相同} 表示找的 y 的位数是 n,取 x个1构成的 y的个数。

    那ans+=dp[n,x,1]+dp[n,x,0] (1≤x≤n,且满足num[x]=k-1)

    这样还是会wa的....因为有坑点,当 k=0时,答案为1需特判。

    这样还是会wa的....因为还有坑点,当k=1是,答案需减1,因为原答案会构成一个 y=1,而 1 是 0步就可以搞定的,但num[1]=0=k-1 会被统计。

    这样还是会wa的....那我也没办法了,因为没有坑点啦~~(≧▽≦)/~啦啦啦

     1 const HR=1000000007;
     2 var s:ansistring;
     3     i,j:longint;
     4     n,k,x,y,ans:longint;
     5     dp:array[0..1050,0..1050,0..2]of longint;
     6     num:array[0..1050]of longint;
     7 begin
     8   readln(s);
     9   n:=length(s);
    10   read(k);
    11   if k=0 then
    12   begin
    13     writeln(1);
    14     exit;
    15   end;
    16   for i:=2 to n do
    17   begin
    18     x:=i;
    19     y:=0;
    20     while x>0 do
    21     begin
    22       inc(y,x mod 2);
    23       x:=x>>1;
    24     end;
    25     num[i]:=num[y]+1;
    26   end;
    27   dp[0,0,0]:=1;
    28   for i:=1 to n do
    29   begin
    30     dp[i,0,1]:=1;
    31     for j:=1 to i do
    32     begin
    33       if s[i]='1' then
    34       begin
    35         dp[i,j,0]:=dp[i-1,j-1,0];
    36         dp[i,j,1]:=dp[i-1,j,0]+dp[i-1,j-1,1]+dp[i-1,j,1];
    37       end else
    38       begin
    39         dp[i,j,0]:=dp[i-1,j,0];
    40         dp[i,j,1]:=dp[i-1,j,1]+dp[i-1,j-1,1];
    41       end;
    42       if dp[i,j,0]>=HR then dp[i,j,0]:=dp[i,j,0] mod HR;
    43       if dp[i,j,1]>=HR then dp[i,j,1]:=dp[i,j,1] mod HR;
    44     end;
    45   end;
    46   for i:=1 to n do
    47   if num[i]=k-1 then
    48   begin
    49     inc(ans,(dp[n,i,1]+dp[n,i,0]));
    50     if ans>=HR then ans:=ans mod HR;
    51   end;
    52   if k=1 then dec(ans);
    53   writeln(ans);
    54 end.
    cf914C

    QAQ最近的cf的c题都好难...题解一直翻啊...

    27.cf 894C  状压dp (难)

    这cf怎么一道比一道难啊qwq...老年菜兔撑不住了QAQ哇的一声哭出来,div2C怎么就状压了啊QAQ

    根据以往,可以推出,我看题解了...但是这次官方题解真的是萌比,完全看不懂,大致获取信息,类似状压的dp+素数个数很少。

    然后就只能自己想了qwq,反正能练一下状压...

    对于1~70中的数质因数分解。

    dp[i,j] 表示 前 i 个数拼出素数状态为 j 的方案数,状态 j 表示 素数个数,是偶数就是0,是奇数就是1。

    初始化 dp[0,0]=1 

    dp[i+1,j]+=dp[i,j]  不取 a[i]

    dp[i+1,j xor c[a[i]]]+=dp[i,j]  取a[i]   xor正好能满足,两个 1 或0 则为0 一个0一个1则为1这样的运算。这样才可以相应的更改状态。

    c[i] 表示 i 质因数分解后对应的 状态 素数个数是偶数就是0,是奇数就是1。

    答案dp[n,0]-1 减去一个空的序列。即都不取的情况。

    这样的效率 O(n*219   因为素数个数有19个。预处理c[i]只要O(70 log2 70 )

    因为时间和空间都会挂啊,先优化空间,用滚动数组就好了...

    这样当然会TLE(第13个test)辣...因为菜兔选手想不到其他写法了...所以就在搞优化,大致优化为计算出1-i 能拼出的状态 j 而不是全部状态 j。 

    这样还是TLE(13)...再考虑优化,把相同的数都搞到一起,这样1-i能拼出的状态会少一些,因为相同的数被xor 2次后就边0 了...所以状态不会增加...

    这样成功的TLE(14) 了...这时菜兔才意识到优化是不存在的了...考虑打表

    考虑认真思考找写法QAQ。假设刚开始所有的数都是不重复的,如果加入一个重复的数,实际上就是让这个重复的数在搞一个相同的dp...

    那,是不是有什么规律。于是开始打表找规律了。

    假设刚开始所有的数都是不重复的,加入一个重复数字后,答案会在原来基础上   *2+1。

    打了多个表后,发现确实是这样的QAQ

    于是强行用规律,先dp出不重复的数 

    效率O(70*219然后在 对于多出的重复数字,每多一个就 *2+1

     1 const HR=1000000007;
     2 var i,j,k:longint;
     3     v:array[0..530000]of boolean;
     4     can:array[0..530000]of longint;
     5     c:array[0..75]of int64;
     6     a,have:array[0..100050]of longint;
     7     num,n,x,z,new:longint;
     8     bool:longint;
     9     dp:array[0..1,0..530000]of longint;
    10 begin
    11   for i:=2 to 70 do
    12   if not v[i] then
    13   begin
    14     inc(num);
    15     for j:=1 to 70 div i do
    16     begin
    17       v[j*i]:=true;
    18       x:=j*i;
    19       z:=0;
    20       while x mod i=0 do
    21       begin
    22         x:=x div i;
    23         inc(z);
    24       end;
    25       if (z mod 2=1) then c[j*i]:=c[j*i] xor (1 << (num-1));
    26     end;
    27   end;
    28   for i:=1 to 70 do
    29   v[i]:=false;
    30   z:=1;
    31   can[z]:=0;
    32   v[0]:=true;
    33   read(n);
    34   for i:=1 to n do
    35   begin
    36     read(a[i]);
    37     inc(have[a[i]]);
    38     if not v[c[a[i]]] then
    39     begin
    40       inc(z);
    41       can[z]:=c[a[i]];
    42       v[c[a[i]]]:=true;
    43     end;
    44   end;
    45   dp[0,0]:=1;
    46   bool:=0;
    47   for i:=1 to 70 do
    48   for k:=1 to have[i] do
    49   begin
    50     new:=z;
    51     for j:=1 to new do
    52     if dp[bool,can[j]]<>0 then
    53     begin
    54       inc(dp[1-bool,can[j]],dp[bool,can[j]]);
    55       if dp[1-bool,can[j]]>=HR then dp[1-bool,can[j]]:=dp[1-bool,can[j]] mod HR;
    56       inc(dp[1-bool,can[j] xor c[i]],dp[bool,can[j]]);
    57       if dp[1-bool,can[j] xor c[i]]>=HR then
    58          dp[1-bool,can[j] xor c[i]]:=dp[1-bool,can[j] xor c[i]] mod HR;
    59       dp[bool,can[j]]:=0;
    60       if not v[can[j] xor c[i]] then
    61       begin
    62         inc(z);
    63         can[z]:=can[j] xor c[i];
    64         v[can[j] xor c[i]]:=true;
    65       end;
    66     end;
    67     bool:=1-bool;
    68   end;
    69   writeln(dp[bool,0]-1);
    70 end.
    cf894C TLE(14)优化可以学一学
     1 const HR=1000000007;
     2 var i,j,k:longint;
     3     v:array[0..530000]of boolean;
     4     can:array[0..530000]of longint;
     5     c:array[0..75]of int64;
     6     a,have:array[0..100050]of longint;
     7     num,n,x,z,new,ans:longint;
     8     bool:longint;
     9     dp:array[0..1,0..530000]of longint;
    10 function min(a,b:longint):longint;
    11 begin
    12   if a<b then exit(a) else exit(b);
    13 end;
    14 begin
    15   for i:=2 to 70 do
    16   if not v[i] then
    17   begin
    18     inc(num);
    19     for j:=1 to 70 div i do
    20     begin
    21       v[j*i]:=true;
    22       x:=j*i;
    23       z:=0;
    24       while x mod i=0 do
    25       begin
    26         x:=x div i;
    27         inc(z);
    28       end;
    29       if (z mod 2=1) then c[j*i]:=c[j*i] xor (1 << (num-1));
    30     end;
    31   end;
    32   for i:=1 to 70 do
    33   v[i]:=false;
    34   z:=1;
    35   can[z]:=0;
    36   v[0]:=true;
    37   read(n);
    38   for i:=1 to n do
    39   begin
    40     read(a[i]);
    41     inc(have[a[i]]);
    42     if not v[c[a[i]]] then
    43     begin
    44       inc(z);
    45       can[z]:=c[a[i]];
    46       v[c[a[i]]]:=true;
    47     end;
    48   end;
    49   dp[0,0]:=1;
    50   bool:=0;
    51   for i:=1 to 70 do
    52   for k:=1 to min(have[i],1)  do
    53   begin
    54     new:=z;
    55     for j:=1 to new do
    56     if dp[bool,can[j]]<>0 then
    57     begin
    58       inc(dp[1-bool,can[j]],dp[bool,can[j]]);
    59       if dp[1-bool,can[j]]>=HR then dp[1-bool,can[j]]:=dp[1-bool,can[j]] mod HR;
    60       inc(dp[1-bool,can[j] xor c[i]],dp[bool,can[j]]);
    61       if dp[1-bool,can[j] xor c[i]]>=HR then
    62          dp[1-bool,can[j] xor c[i]]:=dp[1-bool,can[j] xor c[i]] mod HR;
    63       dp[bool,can[j]]:=0;
    64       if not v[can[j] xor c[i]] then
    65       begin
    66         inc(z);
    67         can[z]:=can[j] xor c[i];
    68         v[can[j] xor c[i]]:=true;
    69       end;
    70     end;
    71     bool:=1-bool;
    72   end;
    73   ans:=dp[bool,0]-1;
    74   for i:=1 to 70 do
    75   for k:=1 to have[i]-1 do
    76   begin
    77     ans:=ans*2+1;
    78     if ans>=HR then ans:=ans mod HR;
    79   end;
    80   writeln(ans);
    81 end.
    cf894C AC

    没想到自己的代码跑的出奇的快,好吧其实也没多快,但至少也是FPC里最快的辣 ~(≧▽≦)/~啦啦啦 (其实只是因为FPC选手少QAQ)

    28.洛谷P1091  (套路)

    这题又是思维题,看了题解QAQ

    实际上正着求一次最长上升子序列长度,倒着求一次最长上升子序列长度。

    dp[i,0]表示正的 dp[i,1]表示倒着的,具体dp就不讲了,前面有讲到。(好像qwq)

    然后枚举 i 使 max=dp[i,1]+dp[i,0]-1 最大就好了 减去一次重复的本身。

    然后答案就是 n-max

     1 var n:longint;
     2     i,j:longint;
     3     dp:array[0..150,0..2]of longint;
     4     a:array[0..150]of longint;
     5     mx:longint;
     6 function max(a,b:longint):longint;
     7 begin
     8   if a>b then exit(a) else exit(b);
     9 end;
    10 begin
    11   read(n);
    12   for i:=1 to n do
    13   begin
    14     read(a[i]);
    15     dp[i,0]:=1;
    16     dp[i,1]:=1;
    17   end;
    18   for i:=1 to n do
    19     for j:=1 to i do
    20     if a[j]<a[i] then dp[i,0]:=max(dp[j,0]+1,dp[i,0]);
    21   for i:=n downto 1 do
    22     for j:=i to n do
    23     if a[j]<a[i] then dp[i,1]:=max(dp[j,1]+1,dp[i,1]);
    24   for i:=1 to n do
    25   if dp[i,1]+dp[i,0]-1>mx then mx:=dp[i,1]+dp[i,0]-1;
    26   writeln(n-mx);
    27 end.
    luoguP1091

    29. cf919D  拓扑+dp (简单dp+拓扑套路)

    可以发现路是长的比路是短的要更优,所以实际上路必然是一个入度为0的点跑到出度为0的点。

    比赛的时候选择直接spfa跑26次最长路。然后比赛的时候没想到自环是环,然后一直wa4,被zn教做兔。

    wa4的时候随便找出了一些错误,然后把判环改成拓扑而不是spfa。

    凌晨1点比赛结束改完交了之后成功 tle(33test)了,然后发现可能被卡spfa了,毕竟spfa玄学效率。

    早上中午起床然后打算改成拓扑的时候随便跑个dp就好了,没必要spfa。

    然后就过了...(跑的还挺快啊qwq)

    dp[i,c] 表示 从某个入度为 0 的点出发到达 i 这个点经过的点中能使 c 这个字符的最大值是多少。

    dp[i,s[i]]=1 (i为入度为0的点)

    接着在拓扑的时候更新一下dp

    dp[y,c]=max(dp[y,c],dp[x,c]+cost)   (若s[y]=c则cost为1不然为0)

    答案就是 max(dp[i,c]) (1≤i≤n,‘a’≤c≤‘z’)

    其实不难qwq,比赛的时候想太多了...

     1 type
     2   node=record
     3       y,next:longint;
     4   end;
     5 var i:longint;
     6     n,m,tot:longint;
     7     c:char;
     8     v:array[0..300500]of boolean;
     9     first,goin:array[0..300500]of longint;
    10     dp:array[0..300500,'a'..'z']of longint;
    11     e:array[0..300500]of node;
    12     q:array[0..5000000]of longint;
    13     x,y:longint;
    14     ans:longint;
    15     s:ansistring;
    16 procedure adde(x,y:longint);
    17 begin
    18   e[tot].next:=first[x];
    19   e[tot].y:=y;
    20   first[x]:=tot;
    21   inc(tot);
    22 end;
    23 function max(a,b:longint):longint;
    24 begin
    25   if a>b then exit(a) else exit(b);
    26 end;
    27 procedure tupo;
    28 var head,tail:longint;
    29     i,now,y,cost:longint;
    30     c:char;
    31 begin
    32   head:=1;
    33   tail:=0;
    34   for i:=1 to n do
    35   if goin[i]=0 then
    36   begin
    37     inc(tail);
    38     q[tail]:=i;
    39     dp[i,s[i]]:=1;
    40   end;
    41   while head<=tail do
    42   begin
    43     now:=q[head];
    44     i:=first[now];
    45     while i<>-1 do
    46     begin
    47       y:=e[i].y;
    48       for c:='a' to 'z' do
    49       begin
    50         if c=s[y] then cost:=1 else cost:=0;
    51         dp[y,c]:=max(dp[y,c],dp[now,c]+cost);
    52       end;
    53       dec(goin[y]);
    54       if goin[y]=0 then
    55       begin
    56         inc(tail);
    57         q[tail]:=y;
    58       end;
    59       i:=e[i].next;
    60     end;
    61     inc(head);
    62   end;
    63   if tail<>n then
    64   begin
    65     writeln(-1);
    66     halt;
    67   end;
    68 end;
    69 begin
    70   readln(n,m);
    71   readln(s);
    72   for i:=1 to n do
    73   first[i]:=-1;
    74   for i:=1 to m do
    75   begin
    76     read(x,y);
    77     if x<>y then
    78     begin
    79       adde(x,y);
    80       inc(goin[y]);
    81     end else
    82     begin
    83       writeln(-1);
    84       exit;
    85     end;
    86   end;
    87   tupo;
    88   for i:=1 to n do
    89     for c:='a' to 'z' do
    90     if dp[i,c]>ans then ans:=dp[i,c];
    91   writeln(ans);
    92 end.
    cf919D

    30.洛谷P1282  简单线性dp (一般,有点套路)

    设 dp[i,j] 表示前 i 个多米诺骨牌的差值为 j 时的最小费用。

    j 可以为负数 ,c++就移一下...

    dp[i,j+a-b]=min(dp[i-1,j])

    dp[i,j+b-a]=min(dp[i-1,j]+1)

    考虑空间耗损有点多,就滚动数组优化一下,不优化也可以过~(≧▽≦)/~啦啦啦

     1 var n:longint;
     2     i,j:longint;
     3     a,b:longint;
     4     dp:array[0..1000,-6100..6100]of longint;
     5 function min(a,b:longint):longint;
     6 begin
     7   if a<b then exit(a) else exit(b);
     8 end;
     9 begin
    10   read(n);
    11   for i:=-6000 to 6000 do
    12   dp[0,i]:=maxlongint;
    13   dp[0,0]:=0;
    14   for i:=1 to n do
    15   begin
    16     read(a,b);
    17     for j:=-6000 to 6000 do
    18     dp[i,j]:=maxlongint;
    19     for j:=-6000 to 6000 do
    20     if dp[i-1,j]<>maxlongint then
    21     begin
    22       dp[i,j+a-b]:=min(dp[i,j+a-b],dp[i-1,j]);
    23       dp[i,j+b-a]:=min(dp[i,j+b-a],dp[i-1,j]+1);
    24     end;
    25   end;
    26   for i:=0 to 6000 do
    27   if min(dp[n,i],dp[n,-i])<>maxlongint then
    28   begin
    29     writeln(min(dp[n,i],dp[n,-i]));
    30     exit;
    31   end;
    32 end.
    luoguP1282 无滚动数组
     1 var n:longint;
     2     i,j:longint;
     3     a,b:longint;
     4     dp:array[0..1,-6100..6100]of longint;
     5     bool:longint;
     6 function min(a,b:longint):longint;
     7 begin
     8   if a<b then exit(a) else exit(b);
     9 end;
    10 begin
    11   read(n);
    12   for i:=-6000 to 6000 do
    13   begin
    14     dp[0,i]:=maxlongint;
    15     dp[1,i]:=maxlongint;
    16   end;
    17   dp[0,0]:=0;
    18   bool:=0;
    19   for i:=1 to n do
    20   begin
    21     read(a,b);
    22     for j:=-6000 to 6000 do
    23     if dp[bool,j]<>maxlongint then
    24     begin
    25       dp[1-bool,j+a-b]:=min(dp[1-bool,j+a-b],dp[bool,j]);
    26       dp[1-bool,j+b-a]:=min(dp[1-bool,j+b-a],dp[bool,j]+1);
    27       dp[bool,j]:=maxlongint;
    28     end;
    29     bool:=1-bool;
    30   end;
    31   for i:=0 to 6000 do
    32   if min(dp[bool,i],dp[bool,-i])<>maxlongint then
    33   begin
    34     writeln(min(dp[bool,i],dp[bool,-i]));
    35     exit;
    36   end;
    37 end.
    luoguP1282 滚动数组

    31.洛谷P1020 最长上升序列变形 (数据结构优化dp+套路)

    第一问直接求最长不上升序列即可,由于n较大可以用树状数组或其他奇怪的dp+二分写过

    由于树状数组似乎很少人写,而且我只会树状数组,所以就写了这个。

    因为原来的dp要求 1~i-1 中大于 a[i] 值的dp[j]最大

    利用树状数组可以后缀和求最大值。

    一般求 l , r的最大值用数状数组实现是要 logn2 的效率。

    但是对于这个问题没有必要,因为查询只会查询后缀和的最大值,即只有 l r被固定为最大值了。

    所以修改可以直接取max查询也同理取max。

    注意:若题目需查询 l~r的最大值及单点修改操作,不可采用这个方法,具体的写法自行百度。个人建议线段树实现。

    第二问就是一个套路了,实际上是求最长上升序列。这样保证可以覆盖掉所有数。

    就相当于倒着求一个最长下降序列,所以倒着来个树状数组就行了。

    注意和第一问不同,这里要下降而不是不上升。

     1 var  n,m:longint;
     2      dp,tree,a:array[0..150000]of longint;
     3      i:longint;
     4 function max(a,b:longint):longint;
     5 begin
     6   if a>b then exit(a) else exit(b);
     7 end;
     8 function low(x:longint):longint;
     9 begin
    10   exit(x and -x);
    11 end;
    12 procedure adde(x,d:longint);
    13 begin
    14   while x>0 do
    15   begin
    16     tree[x]:=max(tree[x],d);
    17     dec(x,low(x));
    18   end;
    19 end;
    20 function getmax(x:longint):longint;
    21 var s:longint;
    22 begin
    23   s:=0;
    24   if x=0 then exit(s);
    25   while x<=m do
    26   begin
    27     s:=max(tree[x],s);
    28     inc(x,low(x));
    29   end;
    30   exit(s);
    31 end;
    32 begin
    33   while not eoln do
    34   begin
    35     inc(n);
    36     read(a[n]);
    37     m:=max(m,a[n]);
    38   end;
    39   for i:=1 to n do
    40   begin
    41     dp[i]:=getmax(a[i])+1;
    42     adde(a[i],dp[i]);
    43   end;
    44   writeln(getmax(1));
    45   for i:=1 to m do
    46   begin
    47     tree[i]:=0;
    48     dp[i]:=0;
    49   end;
    50   for i:=n downto 1 do
    51   begin
    52     dp[i]:=getmax(a[i]+1)+1;
    53     adde(a[i],dp[i]);
    54   end;
    55   writeln(getmax(1));
    56 end.
    luoguP1020

    某大佬催更了QAQ,初三开学了呀QAQ有时间就补吧...争取暑假转c++之前填完...

    32.洛谷P1508 入门dp (入门)

    直接金字塔变形...

    不细讲了...嗷

     1 var m,n:longint;
     2     i,j:longint;
     3     dp,a:array[0..250,0..250]of int64;
     4     ans:int64;
     5 function max(a,b:int64):int64;
     6 begin
     7   if a>b then exit(a) else exit(b);
     8 end;
     9 begin
    10   read(m,n);
    11   for i:=1 to m do
    12   begin
    13     dp[i,0]:=-maxlongint;
    14     dp[i,n+1]:=-maxlongint;
    15     for j:=1 to n do
    16     begin
    17       read(a[i,j]);
    18       dp[i,j]:=-maxlongint;
    19       if i=m then dp[i+1,j]:=-maxlongint;
    20     end;
    21     readln;
    22   end;
    23   dp[m+1,0]:=-maxlongint;
    24   dp[m+1,n+1]:=-maxlongint;
    25   dp[m+1,(n+1) div 2]:=0;
    26   for i:=m downto 1 do
    27     for j:=1 to n do
    28     begin
    29       dp[i,j]:=max(dp[i,j],dp[i+1,j]+a[i,j]);
    30       dp[i,j]:=max(dp[i,j],dp[i+1,j-1]+a[i,j]);
    31       dp[i,j]:=max(dp[i,j],dp[i+1,j+1]+a[i,j]);
    32     end;
    33   ans:=-maxlongint;
    34   for i:=1 to n do
    35   ans:=max(dp[1,i],ans);
    36   writeln(ans);
    37 end.
    luoguP1508

     33.cf 31E 就一个简单的取决dp (入门)

    dp[i,j] 表示前 i 个字符 有 j 个是A串的 能拿到的最大和

    那b串的就有 i-j 个

    考虑到和其实可以都拆成每一位上的和

    然后就枚举 i 然后dp要这个 s[i] 去A串还是B串就好啦

    设num表示s[i]这个字符转成数字是多少

    ten[i]表示10的 i 次方(10i

    dp[i+1,j+1]=max(dp[i,j]+num*ten[n-j-1]);

    dp[i+1,j]=max(dp[i,j]+num*ten[n-i+j-1]);

    这些奇奇怪怪的下标画个图就可以推了

    求完dp顺着搞一下每一步的最优都是选哪一个就好了

    倒着应该也行,但是我打挂了...懒的调QAQ

     1 var
     2     n:longint;
     3     s:array[0..50]of char;
     4     ans:array[0..50,0..50]of string;
     5     i,j:longint;
     6     ten:array[0..50]of int64;
     7     num:int64;
     8     dp:array[0..50,0..50]of int64;
     9 function max(a,b:int64):int64;
    10 begin
    11   if a>b then exit(a) else exit(b);
    12 end;
    13 function min(a,b:int64):int64;
    14 begin
    15   if a<b then exit(a) else exit(b);
    16 end;
    17 
    18 begin
    19   readln(n);
    20   ten[0]:=1;
    21   for i:=1 to 2*n do
    22   begin
    23     read(s[i]);
    24     if i<=n then ten[i]:=ten[i-1]*10;
    25   end;
    26   readln;
    27   for i:=0 to 2*n-1 do
    28   begin
    29     num:=ord(s[i+1])-48;
    30     for j:=max(0,i-n) to min(n,i) do
    31     begin
    32       if (j<n)and(dp[i+1,j+1]<dp[i,j]+num*ten[n-j-1]) then
    33         dp[i+1,j+1]:=dp[i,j]+num*ten[n-j-1];
    34 
    35       if (i-j<n)and(dp[i+1,j]<dp[i,j]+num*ten[n-i+j-1]) then
    36         dp[i+1,j]:=dp[i,j]+num*ten[n-i+j-1];
    37     end;
    38   end;
    39   for i:=0 to 2*n-1 do
    40   begin
    41     num:=ord(s[i+1])-48;
    42     for j:=max(0,i-n) to min(n,i) do
    43     begin
    44       if (j<n)and(dp[i+1,j+1]=dp[i,j]+num*ten[n-j-1]) then
    45         ans[i+1,j+1]:=ans[i,j]+'H';
    46       if (i-j<n)and(dp[i+1,j]=dp[i,j]+num*ten[n-i+j-1]) then
    47         ans[i+1,j]:=ans[i,j]+'M';
    48     end;
    49   end;
    50   writeln(ans[2*n,n]);
    51 end.
    cf31E

    34.洛谷P1387 有点不是很常规的dp (较难)

    n比较小怎么写都能水

    但是dp似乎是最优的

    刚开始没想出来只会写个n3的前缀和...看了题解懂了

    可能是我比较少写这类型的题

    f[i,j]表示以 i ,j 这个点为右下角能形成的最大正方形的边长

    看个图就懂了

    所以就是

    f[i,j]=min(f[i,j-1],f[i-1,j],f[i-1,j-1])+1

     1 var
     2    n,m:longint;
     3    i,j:longint;
     4    ans:longint;
     5    f,a:array[0..1000,0..1000]of longint;
     6 function min(a,b:longint):longint;
     7 begin
     8   if a<b then exit(a) else exit(b);
     9 end;
    10 begin
    11   read(n,m);
    12   for i:=1 to n do
    13   begin
    14     for j:=1 to m do
    15     read(a[i,j]);
    16     readln;
    17   end;
    18   for i:=1 to n do
    19   begin
    20     for j:=1 to m do
    21     if a[i,j]=1 then
    22     begin
    23       f[i,j]:=min(min(f[i-1,j],f[i,j-1]),f[i-1,j-1])+1;
    24       if f[i,j]>ans then ans:=f[i,j];
    25     end
    26   end;
    27   writeln(ans);
    28 end.
    luoguP1387

    35.洛谷P1855 二维01背包 

    裸的

    dp[i,j]=max(dp[i-time[k],j-money[k]]+1)

     1 var n,m,t:longint;
     2     i,j,k:longint;
     3     time,mo:array[0..150]of longint;
     4     dp:array[0..250,0..250]of longint;
     5 function max(a,b:longint):longint;
     6 begin
     7   if a>b then exit(a) else exit(b);
     8 end;
     9 begin
    10   read(n,m,t);
    11   for i:=1 to n do
    12   read(time[i],mo[i]);
    13   for k:=1 to n do
    14   begin
    15     for i:=m downto mo[k] do
    16     for j:=t downto time[k] do
    17     dp[i,j]:=max(dp[i-mo[k],j-time[k]]+1,dp[i,j]);
    18   end;
    19   writeln(dp[m,t]);
    20 end.
    luoguP1855

     36.洛谷P1541 多维dp 

    dp[i,j,k,p] 表示用了 i 张 1 的    j 张 2 的    k 张 3 的   p 张 4 的 

    设x=i+j*2+k*3+p*4+1

    dp[0,0,0,0]=a[1]

    dp[i,j,k,p]=max(dp[i-1,j,k,p],dp[i,j-1,k,p],dp[i,j,k-1,p],dp[i,j,k,p-1])+a[x]

    不难...就是多维写起来烦了点,于是数据范围看错了...QAQ

     1 var n,m:longint;
     2     i,j,k,p:longint;
     3     cost:longint;
     4     x:longint;
     5     num:array[0..5]of integer;
     6     dp:array[-1..40,-1..40,-1..40,-1..40]of longint;
     7     a:Array[0..400]of longint;
     8 function max(a,b:longint):longint;
     9 begin
    10   if a>b then exit(a) else exit(b);
    11 end;
    12 begin
    13   read(n,m);
    14   for i:=1 to n do
    15   read(a[i]);
    16   for i:=1 to m do
    17   begin
    18     read(x);
    19     inc(num[x]);
    20   end;
    21   dp[0,0,0,0]:=a[1];
    22   for i:=0 to num[1] do
    23   for j:=0 to num[2] do
    24   for k:=0 to num[3] do
    25   for p:=0 to num[4] do
    26   begin
    27     x:=i+j*2+k*3+p*4+1;
    28     cost:=max(dp[i-1,j,k,p],dp[i,j-1,k,p]);
    29     cost:=max(dp[i,j,k-1,p],cost);
    30     cost:=max(dp[i,j,k,p-1],cost);
    31     dp[i,j,k,p]:=max(dp[i,j,k,p],cost+a[x]);
    32   end;
    33   writeln(dp[num[1],num[2],num[3],num[4]]);
    34 end.
    luoguP1541

     37.洛谷P1137 拓扑上简单dp (拓扑套路+dp)

    dp[i] 表示 以 i 为终点能经过的最大值。

    dp[y]=max(dp[x]+1)

    考虑这个dp不满足无后性,所以跑个拓扑的时候顺便跑dp就好了,可以验证从入度的0的点出发必然比不在入度为0的点出发更优。

     1 type
     2   node=record
     3       y,next:longint;
     4   end;
     5 var n,m:longint;
     6     first,dp,goin,q:Array[0..100500]of longint;
     7     i:longint;
     8     x,y:longint;
     9     e:array[0..200500]of node;
    10     tot:longint;
    11 
    12 procedure adde(x,y:longint);
    13 begin
    14   e[tot].next:=first[x];
    15   e[tot].y:=y;
    16   first[x]:=tot;
    17   inc(tot);
    18 end;
    19 function max(a,b:longint):longint;
    20 begin
    21   if a>b then exit(a) else exit(b);
    22 end;
    23 procedure tupu;
    24 var head,tail,now:longint;
    25     i:longint;
    26 begin
    27   head:=1;
    28   tail:=0;
    29   for i:=1 to n do
    30   if goin[i]=0 then
    31   begin
    32     inc(tail);
    33     q[tail]:=i;
    34     dp[i]:=1;
    35   end;
    36   while head<=tail do
    37   begin
    38     now:=q[head];
    39     i:=first[now];
    40     while i<>-1 do
    41     begin
    42       y:=e[i].y;
    43       dp[y]:=max(dp[y],dp[now]+1);
    44       dec(goin[y]);
    45       if goin[y]=0 then
    46       begin
    47         inc(tail);
    48         q[tail]:=y;
    49       end;
    50       i:=e[i].next;
    51     end;
    52     inc(head);
    53   end;
    54 end;
    55 begin
    56   read(n,m);
    57   for i:=1 to n do
    58   begin
    59     first[i]:=-1;
    60     dp[i]:=0;
    61   end;
    62   for i:=1 to m do
    63   begin
    64     read(x,y);
    65     inc(goin[y]);
    66     adde(x,y);
    67   end;
    68   tupu;
    69   for i:=1 to n do
    70   writeln(dp[i]);
    71 end.
    luoguP1137

    38.洛谷P1272 树形dp(难)

    这题突然懵了QAQ 是自己题意理解的不够好...最后的子树并没有要求一定要保留一开始给的数根...

    设 dp[x,j] 表示 以x为根的树保留结点数为 j 的子树需要删的最小边数。

    dp[x,1]=num[x]         num[x]表示x的度数(入度+出度)。

    dp[x,j]=min(dp[y,k]+dp[x,j-k]-2,dp[x,j])    

    一看上去最不好理解应该是"-2" 。 思考对于 已知 以y 为根的答案后,要转移到 以 x为根的 一定要保留 x->y这条边,而 dp[x,j-k] 中包含了删去这条边的代价。

    接着是考虑dp[y,k]中是否包含x->y这条边,会发现如果他不包含,那转移后不会是最优的,发现如果包含,dp[y,k]又重复包含了一次删去这条边的代价,但是这条边是要保留的,所以就-2表示减掉两个不应该有的代价。

     1 type
     2   node=record
     3       y,next:longint;
     4   end;
     5 var n,p:longint;
     6     i:longint;
     7     x,y:longint;
     8     v:array[0..200]of boolean;
     9     dp:array[0..200,0..200]of longint;
    10     e:Array[0..200]of node;
    11     tot,root,ans:longint;
    12     first:array[0..200]of longint;
    13 function min(a,b:longint):longint;
    14 begin
    15   if a<b then exit(a) else exit(b);
    16 end;
    17 procedure adde(x,y:longint);
    18 begin
    19   e[tot].next:=first[x];
    20   e[tot].y:=y;
    21   first[x]:=tot;
    22   inc(tot);
    23 end;
    24 procedure dfs(x:longint);
    25 var i,j,k:longint;
    26     y:longint;
    27 begin
    28   i:=first[x];
    29   for j:=2 to p do
    30   dp[x,j]:=1 << 25;
    31   while i<>-1 do
    32   begin
    33     y:=e[i].y;
    34     dfs(y);
    35     for j:=p downto 2 do
    36     for k:=1 to j-1 do
    37     dp[x,j]:=min(dp[x,j-k]+dp[y,k]-2,dp[x,j]);
    38     i:=e[i].next;
    39   end;
    40 end;
    41 begin
    42   read(n,p);
    43   for i:=1 to n do
    44   begin
    45     first[i]:=-1;
    46     v[i]:=false;
    47   end;
    48   for i:=1 to n-1 do
    49   begin
    50     read(x,y);
    51     adde(x,y);
    52     v[y]:=true;
    53     inc(dp[x,1]);
    54     inc(dp[y,1]);
    55   end;
    56   for i:=1 to n do
    57   if not v[i] then root:=i;
    58   dfs(root);
    59   ans:=1 <<25;
    60   for i:=1 to n do
    61   ans:=min(ans,dp[i,p]);
    62   writeln(ans);
    63 end.
    luoguP1272

    39.洛谷P1373 思维dp (难)

    由于状态用两个人的分数来存的话必然会tle(mle) 所以考虑转换思维(是的我看题解了)

    原题中的k我以p表示 ,并 p++。(代码中的p没++)

    因为状态实际上只与差有关,所以状态用差来存即可。

    dp[i,j,k,0/1] 表示 终点为点 (i,j)  两人的分数之差为 k 0表示这一步由小A走完   1 表示 这一步由uim走完。

    dp[i,j,k,0]+=dp[i-1,j,k-a[i,j]+p %p,1]+dp[i,j-1,k-a[i,j]+p %p,1]  (两个方向,算一下差值,对于负数可以直接+p处理)

    dp[i,j,k,1]+=dp[i-1,j,k+a[i,j] %p,0]+dp[i,j-1,j+a[i,j] %p,0] (同上两个方向)

    答案即 ans+=dp[i,j,0,1];

     1 const HR=1000000007;
     2 
     3 var n,m,p:longint;
     4     i,j,k:longint;
     5     cost,ans:longint;
     6     dp:array[0..801,0..801,0..16,0..1]of longint;
     7     a:array[0..801,0..801]of longint;
     8 begin
     9   read(n,m,p);
    10   for i:=1 to n do
    11   begin
    12     for j:=1 to m do
    13     begin
    14       read(a[i,j]);
    15       if a[i,j]>=p+1 then a[i,j]:=a[i,j] mod (p+1);
    16       dp[i,j,a[i,j],0]:=1;
    17     end;
    18     readln;
    19   end;
    20   for i:=1 to n do
    21   for j:=1 to m do
    22   for k:=0 to p do
    23   begin
    24     cost:=k-a[i,j]+p+1;
    25     if cost>=p+1 then cost:=cost-(p+1);
    26     dp[i,j,k,0]:=dp[i,j,k,0]+dp[i-1,j,cost,1]+dp[i,j-1,cost,1];
    27     if dp[i,j,k,0]>=HR then dp[i,j,k,0]:=dp[i,j,k,0]mod HR;
    28     cost:=k+a[i,j];
    29     if cost>=p+1 then cost:=cost-(p+1);
    30     dp[i,j,k,1]:=dp[i,j,k,1]+dp[i-1,j,cost,0]+dp[i,j-1,cost,0];
    31     if dp[i,j,k,1]>=HR then dp[i,j,k,1]:=dp[i,j,k,1]mod HR;
    32   end;
    33   for i:=1 to n do
    34   for j:=1 to m do
    35   begin
    36     ans:=ans+dp[i,j,0,1];
    37     if ans>=HR then ans:=ans mod HR;
    38   end;
    39   writeln(ans);
    40 end.
    luoguP1373

    40.洛谷P1537  拆分背包 (套路,难二进制优化背包)

    问题可以转换为是否可以用给定的弹珠能拼出 t/2 的价值。

    t+=x*i (1≤i≤6)

    由于很多价值都是一样的,可以考虑二进制拆分然后直接01背包就好啦。

     1 var two:array[0..20]of longint;
     2     f:array[0..100000]of boolean;
     3     t,z,n:longint;
     4     x:longint;
     5     i,j:longint;
     6     a:array[0..50000]of longint;
     7 begin
     8   two[0]:=1;
     9   for i:=1 to 15 do
    10   two[i]:=two[i-1]*2;
    11   while not eof do
    12   begin
    13     inc(z);
    14     n:=0;
    15     t:=0;
    16     for i:=1 to 6 do
    17     begin
    18       read(x);
    19       t:=t+x*i;
    20       for j:=0 to 15 do
    21       if x>=two[j] then
    22       begin
    23         inc(n);
    24         a[n]:=two[j]*i;
    25         x:=x-two[j];
    26       end;
    27       if x>0 then
    28       begin
    29         inc(n);
    30         a[n]:=x*i;
    31       end;
    32     end;
    33     if t=0 then exit;
    34     writeln('Collection #',z,':');
    35     if t mod 2=1 then
    36     begin
    37       writeln('Can''t be divided.');
    38       writeln;
    39     end else
    40     begin
    41       f[0]:=true;
    42       t:=t div 2;
    43       for j:=1 to t do
    44       f[j]:=false;
    45       for i:=1 to n do
    46       for j:=t downto a[i] do
    47       if f[j-a[i]] then f[j]:=true;
    48       if f[t] then writeln('Can be divided.') else writeln('Can''t be divided.');
    49       writeln;
    50     end;
    51   end;
    52 end.
    luoguP1537

    41.洛谷P1613 倍增dp (倍增难)

    看题解了QAQ菜兔真的菜啊...

    dp[k,i,j] 表示 i 到 j 是否存在 2k 的距离。(bool数组)

    dp[k,i,j]=dp[k-1,i,x] and dp[k-1,x,j]

    初始化就是 dp[0,x,y]=1

    这个dp并没办法解决问题...但是可以发现这个dp之后,就可以将原图转换成一个可以求最短路的图

    若存在 dp[k,i,j]=true 那么 i 和 j 就可以连一条 长度为 1 的边,表示用一次跑路机能到达。

    然后folyd跑最短路就ok辣~

    有时候看到 2要想到倍增!!!

     1 var n,m:longint;
     2     x,y,i,j,k:longint;
     3     dp:array[0..32,0..55,0..55]of boolean;
     4     dis:array[0..55,0..55]of longint;
     5 begin
     6   read(n,m);
     7   for i:=1 to m do
     8   begin
     9     read(x,y);
    10     dp[0,x,y]:=true;
    11   end;
    12   for k:=1 to 32 do
    13   for x:=1 to n do
    14     for i:=1 to n do
    15       for j:=1 to n do
    16       if (dp[k-1,i,x])and(dp[k-1,x,j]) then dp[k,i,j]:=true;
    17   for i:=1 to n do
    18   for j:=1 to n do
    19   if i<>j then
    20   begin
    21     for k:=0 to 32 do
    22     if (dp[k,i,j]) then dis[i,j]:=1;
    23     if dis[i,j]=0 then dis[i,j]:=maxint;
    24   end;
    25   for k:=1 to n do
    26     for i:=1 to n do
    27       for j:=1 to n do
    28       if dis[i,k]+dis[k,j]<dis[i,j] then dis[i,j]:=dis[i,k]+dis[k,j];
    29   writeln(dis[1,n]);
    30 end.
    luoguP1613

     42.洛谷P1681 类似34(可Ctrl+F 查找“34.”)

    基本上一样

    设 f[i,j,0/1] 表示 以 (i,j) 权值为0/1 为右下角的最大边长

    则可以延伸的最大边长就是

    themax=min(f[i-1,j,1/0],f[i,j-1,1/0],f[i-1,j-1,0/1])    (注意1 0 的互换)

    f[i,j,a[i,j]]=themax+1;

     1 var n,m:longint;
     2     i,j:longint;
     3     ans,themax:longint;
     4     f:Array[0..2000,0..2000,0..2]of longint;
     5     a:Array[0..2000,0..2000]of longint;
     6 function min(a,b:longint):longint;
     7 begin
     8   if a<b then exit(a) else exit(b);
     9 end;
    10 begin
    11   read(n,m);
    12   for i:=1 to n do
    13   begin
    14     for j:=1 to m do
    15     begin
    16       read(a[i,j]);
    17       f[i,j,a[i,j]]:=1;
    18     end;
    19     readln;
    20   end;
    21   for i:=1 to n do
    22     for j:=1 to m do
    23     begin
    24       themax:=min(f[i-1,j,1-a[i,j]],f[i,j-1,1-a[i,j]]);
    25       themax:=min(themax,f[i-1,j-1,a[i,j]]);
    26       f[i,j,a[i,j]]:=themax+1;
    27     end;
    28   for i:=1 to n do
    29     for j:=1 to m do
    30     if f[i,j,a[i,j]]>ans then ans:=f[i,j,a[i,j]];
    31   writeln(ans);
    32 end.
    luoguP1681

    43.洛谷P3800 单调队列优化dp (数据结构优化dp)

    dp[i,j] 表示第 i 层 位于第 j 列的最优解

    dp[i,j]=max(dp[i-1,k]+a[i,j]) (j-t≤k≤j+t)

    然后发现这个dp可以单调队列优化,还可以滚动数组优化...

    但是我滚动就懒得写了

     1 var n,m,k,t,c,max:longint;
     2     i,j,x,y:longint;
     3     head,tail:longint;
     4     dp,a:array[0..4500,0..4500]of longint;
     5     q:array[0..4500]of longint;
     6 begin
     7   read(n,m,k,t);
     8   for i:=1 to k do
     9   begin
    10     read(x,y,c);
    11     a[x,y]:=c;
    12   end;
    13   for i:=1 to n do
    14   begin
    15     for j:=0 to m do
    16     q[j]:=0;
    17     head:=1;
    18     tail:=0;
    19     for x:=0 to t do
    20     begin
    21       while (dp[i-1,x]>=dp[i-1,q[tail]])and(tail>0) do dec(tail);
    22       inc(tail);
    23       q[tail]:=x;
    24     end;
    25     for j:=1 to m do
    26     begin
    27       while (q[head]<j-t)and(head<=tail) do inc(head);
    28       if t+j<=m then
    29       begin
    30         while (dp[i-1,t+j]>=dp[i-1,q[tail]])and(tail>=head) do dec(tail);
    31         inc(tail);
    32         q[tail]:=t+j;
    33       end;
    34       if head>tail then dp[i,j]:=a[i,j] else
    35         dp[i,j]:=dp[i-1,q[head]]+a[i,j];
    36     end;
    37   end;
    38   for i:=1 to m do
    39   if dp[n,i]>max then max:=dp[n,i];
    40   writeln(max);
    41 end.
    luoguP3800

    43题辣!~

    44.洛谷P1417 排序dp (贪心+dp)

    很显然一个简单的01背包

    但真的这么容易?还要考虑吧选物品的顺序

    考虑相邻两个物品 i , j 的价值和的选择有

     i 物品先选   a[i]-t*b[i]+a[j]-(t+c[i])*b[j]  ①

     j 物品先选   a[j]-t*b[j]+a[i]-(t+c[j])*b[i]  ②

    那考虑先选 i 的价值更大时

    ①>② 化简得  b[j]*c[i]<b[i]*c[j]

    所以 i 先选时 i 与 j 的先后顺序就可以确定 排序一下即可

     1 var t,n,ans:int64;
     2     i,j:longint;
     3     dp,a,b,c:array[0..500000]of int64;
     4 function max(a,b:int64):int64;
     5 begin
     6   if a>b then exit(a) else exit(b);
     7 end;
     8 procedure qs(l,r:longint);
     9 var i,j:longint;
    10     t,x,y:int64;
    11 begin
    12   i:=l;
    13   j:=r;
    14   x:=b[(l+r)>>1];
    15   y:=c[(l+r)>>1];
    16   repeat
    17     while c[i]*x<b[i]*y do inc(i);
    18     while c[j]*x>b[j]*y do dec(j);
    19     if i<=j then
    20     begin
    21       t:=a[i];a[i]:=a[j];a[j]:=t;
    22       t:=b[i];b[i]:=b[j];b[j]:=t;
    23       t:=c[i];c[i]:=c[j];c[j]:=t;
    24       inc(i);
    25       dec(j);
    26     end;
    27   until i>j;
    28   if l<j then qs(l,j);
    29   if i<r then qs(i,r);
    30 end;
    31 begin
    32   read(t,n);
    33   for i:=1 to n do
    34   read(a[i]);
    35   for i:=1 to n do
    36   read(b[i]);
    37   for i:=1 to n do
    38   read(c[i]);
    39   qs(1,n);
    40   for i:=1 to n do
    41   for j:=t downto c[i] do
    42   dp[j]:=max(dp[j-c[i]]+a[i]-j*b[i],dp[j]);
    43   for i:=1 to t do
    44   ans:=max(ans,dp[i]);
    45   writeln(ans);
    46 end.
    luoguP1417

    45.洛谷P1057 阶段dp 

    f[i,j] 表示传第 i 次 球在 j 手中的方案

    f[i,j]=f[i-1,L]+f[i-1,R]  L为 j 的左边 R为 j 的右边

    初始化 f[0,1]=1 没传之前球在 1号 手里

    答案f[m,1]

     1 var n,m,l,r:longint;
     2     i,j:longint;
     3     f:array[0..50,0..50]of longint;
     4 begin
     5   read(n,m);
     6   f[0,1]:=1;
     7   for i:=1 to m do
     8     for j:=1 to n do
     9     begin
    10       l:=j-1;
    11       if l=0 then l:=n;
    12       r:=j+1;
    13       if r=n+1 then r:=1;
    14       f[i,j]:=f[i-1,l]+f[i-1,r];
    15     end;
    16   writeln(f[m,1]);
    17 end.
    luoguP1057

    46.洛谷P1122 树形dp

    dp[i] 表示 以 i 为根的子树的最大和

    初始值dp[i]=cost[i] 

    dp[i]+=max(dp[x],0) 

    ans=max(dp[i])

    注意一下y=fa的时候要跳过转移,因为不可以拿父亲节点更新儿子节点。

     1 type
     2   node=record
     3       y,next:longint;
     4   end;
     5 var
     6    tot,n,ans:longint;
     7    i:longint;
     8    x,y:longint;
     9    dp,cost,first:array[0..50000]of longint;
    10    e:Array[0..50000]of node;
    11 function max(a,b:longint):longint;
    12 begin
    13   if a>b then exit(a) else exit(b);
    14 end;
    15 procedure adde(x,y:longint);
    16 begin
    17   e[tot].next:=first[x];
    18   e[tot].y:=y;
    19   first[x]:=tot;
    20   inc(tot);
    21 end;
    22 procedure dfs(x,fa:longint);
    23 var i,y:longint;
    24 begin
    25   i:=first[x];
    26   while i<>-1 do
    27   begin
    28     y:=e[i].y;
    29     if y<>fa then dfs(y,x) else
    30     begin
    31       i:=e[i].next;
    32       continue;
    33     end;
    34     dp[x]:=dp[x]+max(dp[y],0);
    35     i:=e[i].next;
    36   end;
    37 end;
    38 begin
    39   read(n);
    40   for i:=1 to n do
    41   begin
    42     read(cost[i]);
    43     dp[i]:=cost[i];
    44     first[i]:=-1;
    45   end;
    46   for i:=1 to n-1 do
    47   begin
    48     read(x,y);
    49     adde(x,y);
    50     adde(y,x);
    51   end;
    52   dfs(1,0);
    53   for i:=1 to n do
    54   ans:=max(ans,dp[i]);
    55   writeln(ans);
    56 end.
    luoguP1122

    47.51nod 1242 矩阵快速幂模板题

    斐波那契用矩阵快速幂优化即可

     1 type
     2   arr=array[1..2,1..2]of int64;
     3 const
     4   HR=1000000009;
     5   t:arr=((1,0),
     6          (0,1));
     7   a:arr=((1,1),
     8          (1,0));
     9 var n:int64;
    10     y,f:arr;
    11 procedure tonew(n,m:int64;var a:arr;b:arr);
    12 var
    13     i,j,k:longint;
    14 begin
    15   for i:=1 to 2 do
    16     for j:=1 to m do
    17     begin
    18       for k:=1 to 2 do
    19       begin
    20         f[i,j]:=f[i,j]+(a[i,k]*b[k,j] mod n);
    21         if f[i,j]>n then f[i,j]:=f[i,j] mod n;
    22       end;
    23     end;
    24   a:=f;
    25   for i:=1 to 2 do
    26     for j:=1 to 2 do
    27     f[i,j]:=0;
    28 end;
    29 function power(b,n:int64):int64;
    30 begin
    31   y:=a;
    32   while b>0 do
    33   begin
    34     if b and 1=1 then tonew(n,2,t,y);
    35     tonew(n,2,y,y);
    36     b:=b >> 1;
    37   end;
    38   f[1,1]:=1;
    39   f[2,1]:=0;
    40   tonew(n,1,t,f);
    41   exit(t[2,1]);
    42 end;
    43 begin
    44   read(n);
    45   writeln(power(n,HR));
    46 end.
    51nod1242

    48.洛谷P1108  dp上dp

    QAQ看题解了...菜兔啊

    第一问很简单...dp[i] 表示最长下降子序列长度

    第二问:  f[i] 表示 以 i 结尾的长度为 dp[i] 的方案数

    f[i]+=f[j] (若 j 能转移到 i 即dp[i]=dp[j]+1 且 a[j]>a[i])

    但是这样没有考虑重复状态,这里的重复是按子序列的数字而不是子序列原下标,所以不好处理...

    考虑 dp[i]=dp[j]a[i]=a[j] 则说明 i 与 j 实际上是同一个方案,那么 i 能转移的k 就只需要加上一次 f[i] 或者加上一次 f[j] 就好了 

    那就强行把 f[j] 置0

     1 var sum,ans:int64;
     2     dp,f,a:Array[0..5500]of int64;
     3     i,j:longint;
     4     n:longint;
     5 function max(a,b:int64):int64;
     6 begin
     7   if a>b then exit(a) else exit(b);
     8 end;
     9 begin
    10   read(n);
    11   for i:=1 to n do
    12   read(a[i]);
    13   a[0]:=maxlongint;
    14   for i:=1 to n do
    15   begin
    16     for j:=0 to i-1 do
    17     if a[j]>a[i] then dp[i]:=max(dp[i],dp[j]+1);
    18   end;
    19   for i:=1 to n do
    20   ans:=max(ans,dp[i]);
    21   f[0]:=1;
    22   for i:=1 to n do
    23   for j:=0 to i-1 do
    24   begin
    25     if (dp[i]=dp[j]+1)and(a[j]>a[i]) then f[i]:=f[i]+f[j];
    26     if (dp[i]=dp[j])and(a[i]=a[j]) then f[j]:=0;
    27   end;
    28   for i:=1 to n do
    29   if dp[i]=ans then inc(sum,f[i]);
    30   writeln(ans,' ',sum);
    31 end.
    luoguP1108

    49.洛谷P1504  01背包

    题目可以转换为在所以城堡中分别选出任意个积木都能拼出的最大高度

    对于每一个城堡都做一个01背包,dp[i] 表示 是否可以拼出 高度为 i 。

    然后用个can[i] 记录有几个 城堡可以拼出高度为 i 

    ans为 can[i]=n 的最大 i

     1 var n:longint;
     2     x:longint;
     3     i,j:longint;
     4     can:Array[0..15000]of longint;
     5     dp:Array[0..15000]of boolean;
     6 begin
     7   read(n);
     8   for i:=1 to n do
     9   begin
    10     x:=0;
    11     dp[0]:=true;
    12     while x>=0 do
    13     begin
    14       read(x);
    15       if x>=0 then
    16       begin
    17         for j:=10000 downto x do
    18         if dp[j-x] then dp[j]:=true;
    19       end;
    20     end;
    21     for j:=1 to 10000 do
    22     if dp[j] then
    23     begin
    24       dp[j]:=false;
    25       inc(can[j]);
    26     end;
    27   end;
    28   can[0]:=n;
    29   for j:=10000 downto 0 do
    30   if can[j]=n then
    31   begin
    32     writeln(j);
    33     exit;
    34   end;
    35 end.
    luoguP1504

    50.洛谷P1754 简单dp

    最后一题辣,简单点咯..

    dp[i,j] 表示 前 i 个人 有 j 个是50元的方案

    dp[i,j]+=dp[i-1,j-1] j>0

    dp[i,j]+=dp[i-1,j]  i-j>=j

    然后ans+=dp[2*n,i] 

    QAQ 统计答案忘记敲个 2* ,于是wa了惨兮兮..

     1 var n:longint;
     2     i,j:longint;
     3     ans:int64;
     4     dp:array[0..50,0..50]of int64;
     5 function min(a,b:longint):longint;
     6 begin
     7   if a<b then exit(a) else exit(b);
     8 end;
     9 begin
    10   read(n);
    11   dp[0,0]:=1;
    12   for i:=1 to 2*n do
    13     for j:=0 to min(i,n) do
    14     begin
    15       if j>0 then dp[i,j]:=dp[i-1,j-1];
    16       if i-j<=j then inc(dp[i,j],dp[i-1,j]);
    17     end;
    18   for i:=0 to n do
    19   inc(ans,dp[2*n,i]);
    20   writeln(ans);
    21 end.
    luoguP1754

    未完待续

    ~完结撒花~

    自己挖的坑,跪着也要填完!!!

    自己挖的坑,终于跪着补完了

  • 相关阅读:
    简单使用Git和Github来管理自己的代码和读书笔记
    js中的事件委托
    join和split的区别
    浮动的清除方式
    图片代替多选(单选)按钮
    js和jquery实现简单的选项卡
    闭包——之初理解
    Django REST framework+Vue 打造生鲜超市(七)
    Django REST framework+Vue 打造生鲜超市(六)
    Django REST framework+Vue 打造生鲜超市(五)
  • 原文地址:https://www.cnblogs.com/Bunnycxk/p/7360183.html
Copyright © 2011-2022 走看看