动态规划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.
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.
因为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.
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.
上面的代码似乎有点问题...
以上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.
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.
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.
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.
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.
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.
对于 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.
大致都已经很明白了就不注释了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.
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.
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.
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.
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.
惨啊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.
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.
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.
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.
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.
懒的筛好题了...只要稍微有些难度的就放进来,不然坑补不完 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.
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.
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.
没想到自己的代码跑的出奇的快,好吧其实也没多快,但至少也是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.
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.
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.
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.
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.
被某大佬催更了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.
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.
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.
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.
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.
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.
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.
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.
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.
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辣~
有时候看到 2k 要想到倍增!!!
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
未完待续
~完结撒花~
自己挖的坑,跪着也要填完!!!
自己挖的坑,终于跪着补完了