动态规划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

 

 

未完待续

~完结撒花~

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

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

posted @ 2017-08-14 21:04  Bunnycxk  阅读(1838)  评论(7编辑  收藏  举报