线性,背包,区间DP例题

发布时间 2023-03-29 16:47:56作者: andy_lz

P1282多米诺骨牌

容易发现一个性质:对于前\(i\)个牌子,它们的点数总和加起来是一个定值。所以,设\(f[i][j]\)表示前\(i\)个多米诺骨牌的第一行的和为j时的最小旋转次数。

状态转移方程:

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

初始化:

\(f[1][a[1]]=0;f[1][b[1]]=1;\)其余全部是正无穷

P4138挂饰

\(f[i][j]\)表示前\(i\)个物品,剩余
至少\(j\)个空挂钩时的最大喜悦值。

状态转移方程:

\[f[i][j]=min(f[i-1][j],f[i-1][max(j-p[i].a,0)+1]+p[i].b) \]

初始化:\(f[0][1]=0\),其余全部是负无穷

需要注意的是,在\(DP\)前要按照挂钩的数量从大到小排序,因为排序之后才能使挂钩数量尽可能多,越有可能使得结果更优。如果不排序,可能很快就没有了挂钩,错过了后面权值特别大的点。

P2679子串

\(f[i][j][k]\)表示从A串的前i个字符中取k个不重复子串,拼接起来后与B的前j个子串相等的方案数量。

状态转移方程:

\(f[i][j][k]=\)

\(f[i-1][j][k],if(a[i]!=b[j])\)

\(f[i-1][j][k]+f[i-1][j-1][k-1],if(a[i]==b[j],a[i-1]!=b[j-1])\)

\(f[i-1][j][k]+f[i-1][j-1][k-1]+f[i-1][j-2][k-1],if(a[i]==b[j],a[i-1]==b[j-1],a[i-2]!=b[j-2])\)

......

如果直接算,时间复杂度是\(O(nm^2k)\),明显超时。这时我们可以用前缀和来优化\(DP\),时间复杂度会降至\(O(nmk)\)

另外,此时的空间复杂度为\(1000\times200\times 200\times2\)(开\(long\) \(long\)),也就是8e7,也会爆炸。观察式子,我们可以发现每一次转移都是\(f[i-1][...]\)转移到\(f[i][...]\),所以我们可以直接滚掉第一维,将空间复杂度优化到\(200*200*2\)

P5662 [CSP-J2019] 纪念品

题目中有一句关键的话“每天卖出纪念品换回的金币可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。”我们可以根据这句话简化问题:把第\(i\)天买入,第\(j\)天卖出看作:第\(i\)天买入,第\(i+1\)天卖出,第\(i+1\)天买入,第\(i+2\)天卖出......第\(j-1\)天买入,第\(j\)天卖出。这时就可以将当天的价格看作体积,将当天与后一天的价格差当做价值,进行完全背包。

设f[i][j][k]表示第i天,前j个物品,手中的钱数为k时候的最大收益。状态转移方程:

\[f[i][j][k]=max(f[i][j][k],f[i][j-1][k+p[i][j]]+p[i+1][j]-p[i][j]) \]

考虑滚动数组,我们发现只需要保留\(k\)这一维即可。

P4059找爸爸

首先要知道,两个序列的某一个位置都是空格的情况肯定不是最优解。因为题目中k的系数是负数,k增加会令答案更劣。所以对于一个位置只考虑三种情况:

1:\(A\)\(B\)都不是空格

2:\(A\)是空格,\(B\)不是

3:\(A\)不是空格,\(B\)

\(dp[i][j][0/1/2]\)表示\(A\)\(0\)$i-1$和$B$的$0$\(j-1\)匹配,且最后一位分别是情况\(1,2,3\)时的最优解。

初始化:\(dp[0][i][1]=-a-b*(i-1),dp[0][i][0]=dp[0][i][2]=-1e9\)

\(dp[i][0][2]=-a-b*(i-1),dp[i][0][0]=dp[i][0][1]=-1e9\)

\(dp[0][0][1]=dp[0][0][2]=-1e9\)

状态转移方程:

\(f[i][j][0]=max(f[i-1][j-1][0],max(f[i-1][j-1][1],f[i-1][j-1][2]))+d[x[i-1]][y[j-1]];\)

\(f[i][j][1]=max(f[i][j-1][0]-a,max(f[i][j-1][1]-b,f[i][j-1][2]-a));\)

\(f[i][j][2]=max(f[i-1][j][0]-a,max(f[i-1][j][1]-a,f[i-1][j][2]-b));\)

P7961 [NOIP2021]数列

考虑按照 \(S\) 的二进制位数进行 \(DP\)

\(f[i][j][k][l]\) 表示当前是 \(S\) 从低到高的第 \(i\) 位,已经确定了 \(j\) 个序列 \(a\) 中的元素, \(S\) 从低到高前 \(i\) 位中有 \(k\)\(1\) ,向第 \(i\) 位的下一位进位 \(l\)

如果从前面转移到 \(f[i][j][k][l]\) ,细节太多,不好写。所以考虑从 \(f[i][j][k][l]\) 往后转移。

假设当前序列 \(a\) 中分配了 \(t\) 个元素的值为 \(i\) ,加上上一位进过来的 \(l\)\(1\) ,总共要向下一位进位\(t+l\),下一位进位的时候要进位 \((t+l)/2\) .

\(f[i][j][k][l]\) 往后要转移的状态是: \(f[i+1][j+t][k+(t+l)\) \(mod\) \(2][(l+p)/2]\)

状态转移方程:

\(f[i+1][j+t][k+(t+l)\) \(mod\) \(2][(l+p)/2]\)

\(=f[i][j][k][l]\times v^t_i\times C(n-j,t)\)