记录一些CF-DP题(qwq)

本文深入讲解了DP算法在解决特定问题中的应用,如篮球小哥哥身高总和最大化、管道铺设成本最小化、数列合并最小长度及木板高度调整最小花费等问题。通过定义状态和转移方程,详细解析了每道题目的解题思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CodeForces 1195C Basketball Exercise

  • 题意:有两排小哥哥,每排都有 n n n个小哥哥。我们要从这 2 n 2n 2n个小哥哥里选择任意个,使得这些小哥哥的身高总和最大(花痴脸). 限制我们选择的小哥哥不能是一排中相邻的两个。
  • 思路:定义 d p [   ] [ 3 ] dp[ \ ][3] dp[ ][3]其中 d p [ p o s ] [ 0 ] dp[pos][0] dp[pos][0]表示 p o s pos pos位置上不选人, d p [ p o s ] [ 1 ] dp[pos][1] dp[pos][1]表示选择 p o s pos pos位置上第一排的小哥哥, d p [ p o s ] [ 2 ] dp[pos][2] dp[pos][2]表示选择 p o s pos pos位置上第二排的小哥哥。
  • 很容易得到 d p dp dp方程如下:
  1. d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 2 ] ) ; dp[i][0]=max(dp[i-1][0],dp[i-1][1],dp[i-1][2]); dp[i][0]=max(dp[i1][0],dp[i1][1],dp[i1][2]); //这个位置不选,那么上个位置不选或选谁都无所谓
  2. d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 2 ] ) + h [ 0 ] [ i ] ; dp[i][1]=max(dp[i-1][0],dp[i-1][2])+h[0][i]; dp[i][1]=max(dp[i1][0],dp[i1][2])+h[0][i]; //第 i i i位选第一排的,那么第 i − 1 i-1 i1位上一定不能选第一排的
  3. d p [ i ] [ 2 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] ) + h [ 1 ] [ i ] ; dp[i][2]=max(dp[i-1][0],dp[i-1][1])+h[1][i]; dp[i][2]=max(dp[i1][0],dp[i1][1])+h[1][i]; //第 i i i位选第二排的,那么第 i − 1 i-1 i1位上一定不能选第二排的

CodeForces 1207C Gas Pipeline

  • 题意:来,我们铺管道,立柱子!一个单位的管道需要花费 a a a,一个单位的柱子需要花费 b b b. 但是呢,不是所有时候都天时地利人和的,所以如果遇到路口,那么就要必须要架高管道。给出一个 01 01 01串,如果对应位上是 1 1 1说明必须将管道架高到高度2. 问最小花费是多少呢?【建议结合第二个样例理解题意】
  • 思路:必要的花费是 m u s t = a ∗ n + b ∗ ( n + 1 ) must=a*n+b*(n+1) must=an+b(n+1),这个毋庸置疑。我们可以调整的是:在0的地方我们是选择继续在高度为2的地方继续铺管道,还是选择下降到高度为1的地方铺管道呢?
    – 如果是 00 00 00那么我们可以上升,消耗是 a + b a+b a+b,因为既需要管道也需要柱子;也可以保持高度1,消耗为0
    – 如果是 01 01 01那么我们就必须上升,消耗是 a + b a+b a+b
    – 如果是 10 10 10那么我们可以下降,消耗是 a a a,也可以保持高度 2 2 2,消耗是 b b b
    – 如果是 11 11 11那么我们必须保持高度2,消耗是 b b b
  • 由此我们可以很容易想到定义 d p [ p o s ] [ 2 ] dp[pos][2] dp[pos][2],其中 d p [ p o s ] [ 0 ] dp[pos][0] dp[pos][0]是操作 p o s pos pos位的管道在高度1, d p [ p o s ] [ 1 ] dp[pos][1] dp[pos][1]是操作 p o s pos pos位的管道在高度2. 那么:
  1. 如果 p o s pos pos位是 1 1 1 //那该位置不可能为0
    d p [ p o s ] [ 0 ] = I N F ; dp[pos][0]=INF; dp[pos][0]=INF;
    d p [ p o s ] [ 1 ] = m i n ( d p [ p o s − 1 ] [ 0 ] + a + b , d p [ p o s − 1 ] [ 1 ] + b ) ; dp[pos][1]=min(dp[pos-1][0]+a+b, dp[pos-1][1]+b); dp[pos][1]=min(dp[pos1][0]+a+b,dp[pos1][1]+b);
  2. 如果 p o s pos pos位是 0 0 0
    d p [ p o s ] [ 0 ] = m i n ( d p [ p o s − 1 ] [ 1 ] + a + b , d p [ p o s − 1 ] [ 0 ] ) ; dp[pos][0]=min(dp[pos-1][1]+a+b, dp[pos-1][0]); dp[pos][0]=min(dp[pos1][1]+a+b,dp[pos1][0]);
    d p [ p o s ] [ 1 ] = m i n ( d p [ p o s − 1 ] [ 0 ] + a + b , d p [ p o s − 1 ] [ 1 ] + b ) ; dp[pos][1]=min(dp[pos-1][0]+a+b, dp[pos-1][1]+b); dp[pos][1]=min(dp[pos1][0]+a+b,dp[pos1][1]+b);

CodeForces 1312E Array Shrinking

  • 题意:相邻的两个相等的数x可以合并为x+1,问可以通过合并操作得到的数列最小长度?
  • 思路:我们定义 d p [ r ] dp[r] dp[r]表示区间 [ 1 , n ] [1,n] [1,n]可缩并得到的最小长度。对于右端点 r r r,我们可以从右端点开始枚举左端点 l l l,看区间 [ l , r ] [l,r] [l,r]是不是可以被合并为长度1. 如果可以显然 d p [ r ] = d p [ l − 1 ] + 1 dp[r]=dp[l-1]+1 dp[r]=dp[l1]+1.

那么我们如何判断一个区间是不是可以被合并呢?
这个思路的重点是什么?是从右端点开始枚举左端点 .

我们定义now为枚举到的左端点 a [ l ] a[l] a[l],并且定义一个栈,栈中的内容是我们枚举到的区间 [ l , r ] [l, r] [l,r]缩并完之后的剩下的值。那么对于当前枚举到的 l l l来说,是不是栈中的元素是区间 [ l + 1 , r ] [l+1,r] [l+1,r]缩并后的?

显然,如果now等于栈顶元素,那么就踢掉栈顶(合并栈顶元素和now),now自增1. 直到now不能合并为止(包括两种情况:栈空 或者 now ≠ \neq = 栈顶元素)

这个写法,大家可以自行考虑~hhh,窝觉得超级妙欸

CodeForces 1221D Make The Fence Great Again

  • 题意:有 n n n个木板,我们必须要使得相邻两个木板的高度不同。增高木板不限次,问最小花费?
  • 思路:我们可以发现,每块木板的高度增加一定不会超过两次。(可以自己举例验证)所以我们我们可以选择定义数组 d p [ p o s ] [ 3 ] dp[pos][3] dp[pos][3](0\1\2分别表示保持原高度,高度增加1,高度增加2)表示截止到位置 p o s pos pos,并且位置 p o s pos pos的木板增加高度0\1\2的最小花费。
  • 我们遍历一遍所有的木板
  1. 如果 p o s pos pos位置上的木板和前面的木板高度相同,那么 d p [ p o s ] [ h ] = m i n ( d p [ p o s − 1 ] [ a d d ] + a d d ∗ c o s t [ i ] ) , 其 中 a d d ≠ h dp[pos][h]=min(dp[pos-1][add]+add*cost[i]),其中add \neq h dp[pos][h]=min(dp[pos1][add]+addcost[i]),add=h.
  2. 如果不相同,那么首先 d p [ p o s ] [ a d d ] = d p [ p o s − 1 ] [ a d d ] + a d d ∗ c o s t [ i ] dp[pos][add]=dp[pos-1][add]+add*cost[i] dp[pos][add]=dp[pos1][add]+addcost[i],然后判断当前木板可行的其他两个高度是不是和前一个木板相同。
  • 举个栗子
    首先继承: d p [ p o s ] [ 2 ] = d p [ p o s − 1 ] [ 2 ] + 2 ∗ c o s t [ p o s ] dp[pos][2]=dp[pos-1][2]+2*cost[pos] dp[pos][2]=dp[pos1][2]+2cost[pos],然后判断其他两个高度 h e i g h t [ p o s − 1 ] height[pos-1] height[pos1] h e i g h t [ p o s − 1 ] + 1 和 h e i g h t [ p o s ] + 2 height[pos-1]+1和height[pos]+2 height[pos1]+1height[pos]+2是不是相同,如果不相同,那么更新 d p [ p o s ] [ 2 ] dp[pos][2] dp[pos][2]为可能的最小值【注意要用 d p [ p o s − 1 ] [   ] + 2 ∗ c o s t [ p o s ] dp[pos-1][ \ ]+2*cost[pos] dp[pos1][ ]+2cost[pos]再和 d p [ p o s ] [ 2 ] dp[pos][2] dp[pos][2]比较】。 d p [ p o s ] [ 0 ] dp[pos][0] dp[pos][0] d p [ p o s ] [ 1 ] dp[pos][1] dp[pos][1]类似。

  • MyCode

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值