看完 《那些日子》

     昨晚终于看完 云风 的 那些日子 了。 其实一两个月之前就开始看了,疯看到第十九篇,觉得热血沸腾,然后狂学了一阵子python。一直没再去看,怕脑子热过了又忘了,似乎有些东西一直惦记着比较有吸引力。但最近几天快放假了,一切又都松懈下来,于是把最后2篇都看完了。再次提醒自己那一个梦,做游戏的梦。

     小时候我就是一个挺自闭的孩子,爷爷说这孩子就是乖,自己安静的在旁边摆扑克玩。也不知道为什么,我竟然有那种想法,摆扑克做国家之间的军事斗争。不过估计大多数男孩都有这种摆些玩具打大战的思维吧。好好学习,自娱自乐,简单地过了很多年。直到初中,跟同学去了网吧,在局域网里面打红警。我很惊奇地发现原来这个世界有这么神奇的东西:电脑?不,那时候只当它是个游戏机。初中是比较悠闲的,杂七杂八看了很多书,一周打一回游戏,挺平安地考上高中。高中三年我只上了一个晚上的网,好像是在打红警,过后聊起,兄弟们对我的生存状态表示灰常惊讶。

     上大学的时候也是乱报志愿,但很高兴进了自己还算喜欢的专业:软件工程。不过国内的教育真是...因人设岗,真正技术的东西很大程度上不能依靠老师,得自己去摸索。可惜喜欢做乖孩子的我这方面觉醒晚了点,很快大三找工作的热潮袭来。得思考自己将来干什么的问题了,我喜欢编程,想做个程序员,但程序员也可以做很多方面的工作,现在无论公司还是政府都有IT部门。

     这时候,火爆的游戏产业吸引了我。大学期间,我也常玩游戏,但自我感觉还是很好的,不能算是沉迷游戏的人。我大二的时候教舍友打魔兽,大四的时候有一个已经是魔兽校队了,而我却常被用50%血狂虐。我主要玩的单机游戏和局域网游戏:三国志、红警、魔兽、CS、文明等等。这些游戏,一方面是自己的调节,学累了玩会,玩累了学会;另一方面是大家在一块能有的玩有的聊,算是个社交吧。至于网游,不敢碰啊,校园网慢是一方面,另一方面:看着玩网游的同学疯狂地聚在一起讨论,好像无上限地升级,我就觉得进去会是个无底洞啊。另外,砍怪好无聊啊。

      刚开始学java时有了图形界面,就和宿舍的同学一人编一个贪吃蛇游戏玩。很简陋,很有趣,玩一阵子就把源码弄丢了,那时候还没有养成保存资料的好习惯。我觉得编游戏是一件很有挑战性的事情,实时性要求,大量的数据,图形学,网络协议。人算不如天算,没想到后来竟然有我的保研名额,于是又继续留在学校里面了。但做游戏这个想法已经扎根了,研究生期间也努力做些相关的工作,如模拟驾驶项目、重学c++,学习图形学,自学python。不过胳膊扭不过大腿,现在还得跟老师在专业方向上做点东西,也不知道算不算南辕北辙。

      游戏的确扩展了人生,原来很多你不能经历的事情,现在你可以虚拟地去体验它。研究生期间,白天学习干活,晚上10点回到宿舍大家常常打局dota睡觉,感觉也挺惬意的。问将来,似乎有点想法,有些方向,希望如愿。

      人生如戏,只不过不能存盘读盘吧了,这也是他最有趣的地方。

<think>好的,我现在需要解决这个问题:给定n天和最大停留天数k,规划登山行程,确保不错过指定的登山日,同时总停留天数不超过k,求最少移动次数。这个问题看起来有点挑战性,我需要仔细分析。 首先,问题描述可能需要更明确。假设每天有一个状态,是否适合登山。比如,某些天是必须登山的指定日,而其他天可以选择停留在大本营或者移动。目标是安排这些天的停留和移动,使得在指定日必须停留(即登山),同时总停留天数不超过k,并且移动次数最少。 不过,问题中的“移动次数”具体指什么?比如,从大本营出发去登山再返回算一次移动?或者每次改变状态(停留到移动或反之)算一次移动?可能需要假设每次从停留转为移动,或者移动转为停留都算一次移动次数。例如,连续停留多天只算一次移动,或者每次移动都算一次? 可能题目中的“移动次数”是指从停留状态变为移动状态的次数,或者相反。例如,如果连续停留多天,这期间没有移动,那么可能不算移动次数。而每次需要移动时,比如从停留转为移动,就算一次移动次数。这可能需要更明确的定义。 假设问题中的移动次数是指从停留到移动或移动到停留的状态转换次数。例如,如果某天停留,第二天也停留,没有移动次数增加;如果某天停留,第二天移动,则移动次数加1。反之亦然。因此,最少化移动次数意味着尽可能减少状态切换的次数。 另一个关键点是,必须确保在指定登山日停留,即这些天必须处于停留状态。同时,总停留天数(包括指定日和其他停留日)不能超过k。需要在这些条件下,找到移动次数最少的方案。 可能需要将问题建模为一个动态规划问题。状态可能包括当前天数、已停留的天数、当前状态(停留或移动),以及需要覆盖的指定日。 例如,动态规划的状态可以定义为dp[i][j][s],表示处理到第i天,已经停留了j天,当前状态为s(0表示移动,1表示停留),所需的最小移动次数。然后,状态转移需要考虑下一天是停留还是移动,同时满足指定日的停留要求。 但首先需要明确指定登山日的位置。题目中提到“不错过指定登山日”,但并未给出具体的指定日列表。可能假设指定登山日是给定的某些特定日期,需要在规划中确保这些日期必须处于停留状态。或者,可能指定登山日是需要自己确定的,但题目中的描述不太清楚。 根据问题描述,可能是指存在一个或多个指定的登山日,必须在这些天停留,而其他天可以选择停留或移动,但总停留天数不超过k,且需要计算最少的移动次数。 例如,假设有n天,其中有m个指定的登山日(必须停留),总停留天数不能超过k,其中m ≤ k。需要在非指定日选择是否停留,使得总停留天数不超过k,同时移动次数最少。 这里可能需要明确几个点: 1. 指定登山日的数量是否已经包含在k中?即,k是否包括必须停留的那些天?比如,如果有m个指定日,那么总停留天数不能超过k,而m必须≤k。 2. 移动次数的计算方式。例如,初始状态可能是移动或停留?或者,初始状态不计入移动次数?比如,假设第一天开始于移动状态,那么如果在第一天停留,则算一次移动(从移动到停留)。 或者,移动次数可能指从停留转为移动的次数,或者相反。例如,每次改变状态就增加移动次数一次。因此,连续停留或移动不会增加次数,只有切换时才会。 假设每次状态变化(停留到移动或移动到停留)算一次移动次数。那么,初始状态如果是停留,则不计入,但如果是移动转为停留,则算一次。或者,初始状态可能需要被考虑。例如,如果第一天是停留,则初始状态为停留,否则为移动。 这种情况下,动态规划的状态需要记录当前的状态(停留或移动),以及已停留的天数,已经处理到第几天,同时确保指定日必须停留。 现在,假设问题的输入是n天,其中某些天是必须停留的(指定登山日),总停留天数不能超过k,求最少的移动次数(即状态变化的次数)。 例如,假设指定登山日的集合为S,问题转化为:安排每一天的状态(停留或移动),使得: - 对于所有d ∈ S,状态必须为停留。 - 总停留天数(包括S中的天数和其他停留天数)不超过k。 - 移动次数最少(即状态变化的次数最少)。 这似乎是一个典型的动态规划问题,因为当前决策(停留或移动)会影响后续的决策,并且存在多个约束条件(总停留天数、必须停留的日)。 因此,动态规划的状态可能需要包括: - 当前处理到第i天。 - 当前已使用的停留天数j。 - 当前的状态s(停留或移动)。 此外,必须确保在指定日必须停留,因此在这些天,状态必须为停留。这可能需要在状态转移时进行强制处理。 因此,动态规划的状态转移可以如下: 对于每一天i: 如果i是必须停留的日(d ∈ S): 那么在这一天必须选择停留。此时,如果前一天的状态是停留,那么移动次数不变;如果前一天的状态是移动,那么移动次数加1。同时,已停留天数j需要加1(假设之前已经用了j-1天)。 否则: 可以选择停留或移动。 如果选择停留: - 如果前一天的状态是停留,移动次数不变;否则,移动次数+1。 - 已停留天数j+1。 需要满足j+1 ≤k. 如果选择移动: - 如果前一天的状态是移动,移动次数不变;否则,移动次数+1. 已停留天数j不变。 因此,动态规划的状态转移方程为: 对于每个i,j,s(当前状态): 如果当前i是必须停留日: 必须选择停留。所以新的状态s'是停留。 计算新的移动次数:如果前一天的状态s_prev != 停留,则移动次数+1,否则不变。 同时,j必须增加1(因为必须停留)。 否则: 可以选择停留或移动。 对于选择停留的情况: 检查j+1 ≤k. 移动次数的变化:如果当前状态s_prev是移动,则次数+1;否则不变。 对于选择移动的情况: 移动次数变化:如果当前状态s_prev是停留,次数+1,否则不变。 现在,需要初始化这个动态规划数组。例如,第0天(初始状态)可能需要处理。初始时,可能没有使用任何停留天数,状态可以是移动或停留,但需要考虑初始状态是否算一次移动。例如,假设初始状态是移动,那么如果在第1天停留,则移动次数增加1。或者,初始状态是否计入移动次数可能需要明确。 另外,必须确保所有指定日都被处理为停留状态,否则该路径是无效的。 现在,如何处理这个问题?可能的步骤: 1. 确定所有必须停留的日子,存储在一个集合中,比如S。 2. 初始化动态规划表,dp[i][j][s],其中i的范围是0到n,j的范围是0到k,s是0或1(0表示移动,1表示停留)。 3. 初始条件:dp[0][0][0] = 0(第0天,已用0天,状态为移动,移动次数0);或者可能需要不同的初始条件。例如,如果第1天可以选择停留或移动,初始状态可能影响后续。 或者,可能需要将初始状态设为某种虚拟状态,例如,在开始之前的状态为移动,这样第1天的选择会影响移动次数。 例如,初始状态(i=0)是虚拟的,此时状态为移动,已停留0天,移动次数0。 然后,对于第1天到第n天进行处理。 当处理到第i天时,状态s_prev可以是移动或停留,根据前一个状态来转移。 现在,举一个例子: 假设n=3天,k=2,必须停留的是第3天。 那么,总停留天数不能超过2,且第3天必须停留。因此,可能的方案: 第3天必须停留,所以至少需要1天停留。剩下的1天可以在其他两天中选择停留,或者不选。 假设其他两天可以选择停留与否。比如,第1天停留,第2天移动,第3天停留:总停留天数为2,移动次数为两次(第1天从初始移动转为停留,第3天从移动转为停留)。 或者,第2天停留,第3天停留:总停留天数2,移动次数两次(初始到第2天转为停留,第2到第3天保持停留,所以可能次数为1?可能我之前的理解有误。) 这里可能需要重新理解移动次数的计算方式。比如,初始是移动状态,第1天选择停留,移动次数+1。第2天选择停留,状态未变,移动次数不变。第3天必须停留,状态未变,次数不变。总移动次数是1次。 或者,如果第2天选择移动,则第1天停留,第2天移动,移动次数+1,第3天必须停留,移动次数+1。总次数2次,总停留天数2(第1天和第3天)。 或者,假设初始是移动状态,那么: 如果第1天停留:次数+1(移动到停留)。 第2天移动:次数+1(停留到移动)。 第3天必须停留:次数+1(移动到停留)。 总次数3次,停留天数2(第1和3天)。这符合k=2的条件,但移动次数较多。 或者,如果在第2天不移动,保持停留,则: 第1天停留:次数1。 第2天停留:次数不变。 第3天停留:次数不变。 总次数1次,总停留天数3(但k=2,这不可行)。 所以,在这种情况下,必须选择停留天数总和不超过k=2,并且必须包括第3天。 所以,可行的方案可能是在第3天停留,再加上另一个停留日,如第1或第2天,或者不选。但是,如果必须停留第3天,那么总停留天数至少是1,最多k=2。所以另一个停留日可以是第1或第2天,或者只能其中一个。 例如,选择第3天和第2天停留:总停留天数2,移动次数如何? 初始状态是移动,第2天转为停留(次数+1),第3天保持停留(次数不变)。总次数1次。这样可行? 或者,如果第1天是移动,第2天停留(次数+1),第3天停留(次数不变)。总次数1次,总停留天数2。这符合条件。 所以,这可能是最优的,移动次数为1次。 这似乎需要动态规划来找到最小的移动次数。 现在,回到原问题,如何用动态规划来建模? 状态包括:当前天数i,已停留天数j,当前状态s(0或1)。 转移时,对于每个i,检查是否为必须停留日: 如果是必须停留日,则必须选择停留。此时,如果前一天的状态是停留,则移动次数不变;否则,移动次数+1。同时,已停留天数j必须加1,并且必须满足j+1 ≤k. 否则,可以选择停留或移动: - 选择停留:如果前一天状态是停留,移动次数不变;否则,移动次数+1。同时,j+1必须 ≤k. - 选择移动:如果前一天状态是移动,移动次数不变;否则,移动次数+1. 目标是在处理完所有n天后,找到所有可能的j ≤k,并且满足必须停留的所有天都被处理,对应的移动次数的最小值。 初始化时,可能需要将初始状态设置为s=0(移动),j=0,移动次数0。 然后,对于每个i从1到n: 如果i是必须停留日: 则必须转移到停留状态。 计算新的j' = j +1. 如果j' >k,则此路径无效,跳过。 移动次数的增加取决于前一个状态s_prev是否为停留。 例如,如果前一个状态是移动(s_prev=0),则移动次数增加1;否则不变。 然后,更新dp[i][j'][1]为原dp[i-1][j][s_prev] + delta,其中delta是0或1. 否则,i不是必须停留日: 可以选择停留或移动。 对于停留: j' = j +1. 如果j' >k,不可选。 移动次数的增加:如果前状态是移动,则次数+1;否则不变。 对于移动: j' =j. 移动次数的增加:如果前状态是停留,次数+1;否则不变. 所以,这需要遍历所有可能的j和s_prev,然后计算对应的新状态。 最终,遍历所有可能的j ≤k和s=0或1,在i=n时,找到最小的移动次数。 现在,这个问题的时间复杂度是O(n *k *2),这在n和k较大时可能有问题。例如,n是1e5,k是1e3,这样的复杂度是2e5*1e3=2e8,可能过高。但在实际面试题或算法题中,可能n和k的范围不会太大,或者可以通过优化解决。 不过,原问题是否给出n和k的范围?假设在合理范围内,比如n为1e3,k为1e3,那么O(nk)的算法是可行的。 现在,如何实现这个动态规划? 首先,需要预处理必须停留的日期,比如用数组标记哪些天是必须停留的。 然后,初始化一个二维数组或者三维数组,但由于状态仅依赖前一天的,可以优化空间,例如使用两个二维数组:prev_dp和curr_dp,分别代表i-1和i的状态。 对于每个i,遍历j的可能值,以及前状态s_prev,然后根据是否是必须停留日来决定转移。 例如,初始化prev_dp[j][s]表示处理到i-1天,已停留j天,状态为s时的最小移动次数。初始时,i=0(虚拟天),prev_dp[0][0] =0,其他为无穷大。 然后,对于每个i从1到n: 创建新的curr_dp数组,初始化为无穷大。 遍历j_prev in 0...k: 遍历 s_prev in 0,1: 如果prev_dp[j_prev][s_prev]是无穷大,跳过。 如果当前i是必须停留日: 必须选择停留: s_curr =1. j_curr =j_prev +1. 如果j_curr >k,跳过。 delta = 0 if s_prev ==1 else 1. 新的移动次数 = prev_dp[j_prev][s_prev] + delta. 如果这个次数小于curr_dp[j_curr][s_curr],则更新。 否则: 可以选择停留或移动。 选择停留: s_curr=1. j_curr = j_prev +1. 如果j_curr >k,跳过. delta =0 if s_prev ==1 else 1. new_cost = prev_dp[j_prev][s_prev] + delta. 如果new_cost < curr_dp[j_curr][s_curr],则更新。 选择移动: s_curr=0. j_curr =j_prev. delta =0 if s_prev ==0 else 1. new_cost = prev_dp[j_prev][s_prev] + delta. 如果new_cost < curr_dp[j_curr][s_curr],则更新. 处理完所有可能的j_prev和s_prev后,将curr_dp赋值给prev_dp,继续处理i+1天。 最后,在i=n时,遍历所有j <=k,s=0或1,找到最小的移动次数,并且必须满足所有必须停留的天数都被处理(即,在这些天是否被正确处理)。 但是,如何处理必须停留的天数?例如,在动态规划的过程中,如果某天是必须停留的,则必须在该天选择停留,否则这条路径是无效的。因此,在动态规划的处理中,如果在必须停留的天数选择移动,则该路径被排除,不会被考虑。因此,在代码中,当处理必须停留的天时,只能选择停留,否则跳过。 这样,可以确保所有必须停留的天都被处理为停留状态。 现在,如何测试这个算法的正确性? 比如,假设n=3,k=2,必须停留第3天。那么,可能的路径: i=1,可以选择停留或移动(因为不是必须停留)。 如果第1天选择移动(状态0),第2天选择移动(状态0),第3天必须停留(状态1,移动次数增加1)。 总移动次数为1,停留天数1+1=2(第3天停留,假设另一个停留日可能在第2天?或者,这里可能需要更详细的分析)。 可能我的例子中的条件需要更准确。例如,必须停留第3天,那么总停留天数至少为1。当k=2,可以再停留一天。 比如,路径如下: 天1:移动(状态0),天2:移动(状态0),天3:停留(状态1,次数+1) → 总次数1,停留天数1(但k=2,此时停留天数不够?或者天3的停留必须被计入,因此停留天数至少为1,但k=2允许最多再加1天。) 哦,这里可能有问题。比如,必须停留的天数是m天(比如m=1),而总停留天数不能超过k,所以m ≤k。因此,在动态规划中,必须停留的天数会自动被计入j,因此在处理这些天时,j会加上1。所以,当i=3(必须停留)时,j_prev必须等于 j_curr -1. 例如,在上述例子中,当处理第3天时,必须选择停留,因此j_curr = j_prev +1. 所以,假设初始状态为j=0,状态0(移动)。 处理第1天(非必须停留): 选择停留:j=1,状态1,次数+1(初始是移动→停留)。 或者选择移动:j=0,状态0,次数0. 处理第2天(非必须停留): 如果第1天选择移动(状态0): 第2天可以选择停留(状态1,次数+1,j=1)或继续移动(次数0, j=0). 处理第3天(必须停留): 假设在第2天选择停留: 第3天必须停留,此时j_prev=1,状态_prev=1. 所以,j_curr=2,状态=1. delta=0(因为前状态是1),所以次数是之前次数(第1天移动→第2天停留:次数1)加上0 →总次数1. 总停留天数是2,符合k=2的条件。所以,移动次数为1次。 或者,如果第2天选择移动: 则第3天必须停留,此时j_prev=0(因为第2天是移动,未增加停留天数),状态_prev=0. 处理第3天必须停留:j_curr=1,状态=1,次数为前次数(0) +1=1. 总停留天数是1,这小于k=2。此时还可以在第1天或第2天选择停留。 例如,第1天选择停留(次数1),第2天选择移动(次数2),第3天停留(次数3):总次数3,停留天数2(第1和3天)。 或者,第1天移动,第2天停留(次数1),第3天停留(次数1)→总次数1,停留天数2,符合条件。这是更优的方案。 这说明动态规划可以找到最优解。 综上,动态规划的思路是可行的。 接下来,如何将这个思路转化为C++代码? 首先,需要确定输入格式。假设必须停留的日期被给出为一个数组或集合。例如,给定一个数组S,其中包含必须停留的日期(比如从1到n的某些天)。 然后,预处理这些日期,建立一个布尔数组must_stay[],其中must_stay[i]为true表示第i天必须停留。 接下来,初始化动态规划表。由于空间限制,使用滚动数组优化,只需要两个二维数组:prev_dp和curr_dp,每个都是(k+1) x 2的数组,存储到达该状态的最小移动次数。初始时,将prev_dp初始化为无穷大,除了prev_dp[0][0] =0,表示初始状态(第0天,已停留0天,状态为移动,移动次数0)。 然后,遍历每一天i从1到n: 对于每个i,创建curr_dp并初始化为无穷大。 对于每个可能的j_prev(已停留天数)和s_prev(状态): 如果prev_dp[j_prev][s_prev]是无穷大,跳过。 检查当前天i是否是必须停留: 如果是,必须停留: s_curr =1. j_curr = j_prev +1. 如果j_curr >k,无法转移,跳过。 计算delta:如果s_prev != s_curr,则delta=1,否则0. 新的移动次数是prev_dp[j_prev][s_prev] + delta. 如果这个值小于curr_dp[j_curr][s_curr],则更新。 否则,当前天可以停留或移动: 情况一:选择停留: s_curr =1. j_curr =j_prev +1. 如果j_curr >k,跳过. delta = (s_prev != s_curr) ? 1 :0. 新的移动次数是prev_dp[j_prev][s_prev] + delta. 比较并更新curr_dp[j_curr][s_curr]. 情况二:选择移动: s_curr=0. j_curr =j_prev. delta= (s_prev != s_curr) ?1 :0. 新的移动次数是prev_dp[j_prev][s_prev] + delta. 比较并更新curr_dp[j_curr][s_curr}. 在处理完所有情况后,将curr_dp赋值给prev_dp,进入下一天的处理。 最后,遍历所有可能的j(从m到k,其中m是必须停留的天数)和s,找到最小的移动次数。或者,必须确保所有必须停留的天都被处理,这已经在动态规划过程中保证,因为如果在必须停留的天无法处理(比如j_prev +1超过k),则该路径被跳过,不会被记录。 最后,如果最终存在有效解,则取所有可能的j(可能从m到k)中的最小值。否则,返回-1或其他错误值。 现在,如何处理必须停留的天数总和超过k的情况?例如,如果必须停留的天数m>k,那么问题无解。因此,在预处理阶段,需要首先检查必须停留的天数是否超过k,如果是,直接返回-1。 因此,在代码中,首先计算必须停留的天数m,如果m>k,则无法满足条件,返回-1. 否则,进行动态规划。 综上,代码的大致结构如下: 预处理必须停留的天数m: m = S.size(); if(m >k) return -1; 创建must_stay数组,标记哪些天必须停留. 初始化prev_dp数组,大小为(k+1) x 2,初始化为无穷大,除了prev_dp[0][0] =0. 遍历i从1到n: 创建curr_dp数组,初始化为无穷大. 遍历j_prev从0到k: 遍历s_prev in {0,1}: if prev_dp[j_prev][s_prev] == INF → continue. 如果当前i是必须停留日: s_curr =1. j_curr = j_prev +1. if j_curr >k → skip. delta = (s_prev != s_curr) ?1 :0. curr_dp[j_curr][s_curr] = min(curr_dp[j_curr][s_curr], prev_dp[j_prev][s_prev] + delta). else: 可以选择停留: s_curr=1. j_curr =j_prev +1. if j_curr >k → skip. delta = (s_prev != s_curr) ?1 :0. curr_dp[j_curr][s_curr] = min(..., ...). 选择移动: s_curr=0. j_curr =j_prev. delta= (s_prev != s_curr) ?1 :0. curr_dp[j_curr][s_curr] = min(... , ...). 处理完所有j_prev和s_prev后,将curr_dp复制给prev_dp. 最后,遍历j从m到k, s in {0,1},找到prev_dp[j][s]的最小值. 如果没有找到(所有都是INF),返回-1. 否则,返回该最小值. 现在,在C++中实现这个逻辑需要注意数组的初始化,以及如何高效处理状态。 例如,可以使用两个二维数组,或者使用两个二维的vector,每个大小为(k+1) x 2,存储当前的最小移动次数。 现在,关于空间优化,可以用两个二维数组交替使用。 例如,在C++中: vector<vector<int>> prev_dp(k+1, vector<int>(2, INF)); prev_dp[0][0] =0; for(int i=1; i<=n; i++){ vector<vector<int>> curr_dp(k+1, vector<int>(2, INF)); for(int j_prev=0; j_prev <=k; j_prev++){ for(int s_prev : {0,1}){ if(prev_dp[j_prev][s_prev] == INF) continue; if(must_stay[i]){ //假设i是从1开始的天数,must_stay数组也对应i=1..n int s_curr =1; int j_curr = j_prev +1; if(j_curr >k) continue; int delta = (s_prev != s_curr) ?1 :0; if(curr_dp[j_curr][s_curr] > prev_dp[j_prev][s_prev] + delta){ curr_dp[j_curr][s_curr] = prev_dp[j_prev][s_prev] + delta; } } else { // 选择停留 int s_curr =1; int j_curr =j_prev +1; if(j_curr <=k){ int delta = (s_prev != s_curr) ?1 :0; if(curr_dp[j_curr][s_curr] > prev_dp[j_prev][s_prev] + delta){ curr_dp[j_curr][s_curr] = prev_dp[j_prev][s_prev] + delta; } } // 选择移动 s_curr =0; j_curr =j_prev; int delta = (s_prev != s_curr) ?1 :0; if(curr_dp[j_curr][s_curr] > prev_dp[j_prev][s_prev] + delta){ curr_dp[j_curr][s_curr] = prev_dp[j_prev][s_prev] + delta; } } } } prev_dp = move(curr_dp); } int result = INF; for(int j=m; j<=k; j++){ //m是必须停留的天数 for(int s : {0,1}){ if(prev_dp[j][s] < result){ result = prev_dp[j][s]; } } } if(result == INF) return -1; else return result; 需要注意的是,这里计算m的方式是必须停留的天数总和,例如,must_stay数组中为true的个数。例如: int m=0; vector<bool> must_stay(n+1, false); //天从1到n for(auto day : S){ //假设S是必须停留的天数数组 must_stay[day] = true; m++; } 然后,在预处理时检查if(m>k) return -1. 现在,测试这个代码是否正确。 例如,考虑以下测试用例: 测试用例1: n=3,k=2,必须停留的天数为第3天. 预处理m=1 ≤k=2. 动态规划过程: 初始化prev_dp[0][0] =0. i=1,不是必须停留日: 处理j_prev=0,s_prev=0: 可以选择停留或移动. 停留: j_curr=1,s_curr=1. delta=1. curr_dp[1][1] =0+1=1. 移动: s_curr=0,j_curr=0. delta=0. curr_dp[0][0] =0. 其他j_prev的值都为INF,因此curr_dp after i=1: j=0, s=0 →0. j=1, s=1 →1. 其他为INF. i=2,不是必须停留日: 处理prev_dp中的各个j_prev和s_prev. j_prev=0,s_prev=0: 停留:j_curr=1,s=1 →delta=1 →cost 0+1=1. 移动:j_curr=0 →delta=0 →cost 0. 所以,对于i=2,处理后的curr_dp: 从 j_prev=0,s=0 →移动时,curr_dp[0][0] =0. 停留时,curr_dp[1][1] =1. 同时,处理j_prev=1,s_prev=1(在prev_dp中的值可能为1): 选择停留: j_curr=2,s=1 →delta=0 →cost 1+0=1. 移动: s=0 →delta=1 →cost 1+1=2. 所以,在i=2,curr_dp的状态可能包括: j=0,s=0 →0. j=1,s=1 →1. j=2,s=1 →1. 其他可能还有情况,比如从j_prev=0,s=0,选择停留,导致j=1,s=1,cost=1,但可能被后续的处理覆盖。 现在,当i=3,必须停留日: 处理prev_dp中的各个状态: 假设当前是i=2处理后的prev_dp: 例如,假设prev_dp[j][s]在i=2时有: j=0,s=0 →0. j=1,s=1 →1. j=2,s=1 →1. 对于每个j_prev和s_prev: 当i=3是必须停留,所以只能选择停留。 对于j_prev=0,s_prev=0: j_curr=1,必须≤k=2. delta=1 →因为s_prev=0,s_curr=1. cost=0+1=1. curr_dp[1][1] =1. 对于j_prev=1,s_prev=1: j_curr=2,delta=0 →cost=1+0=1. curr_dp[2][1] =1. 对于j_prev=2,s_prev=1: j_curr=3 →超过k=2,跳过. 此外,其他可能的状态如j_prev=0,s_prev=0的处理后的结果。 所以,在处理完i=3后,curr_dp中的可能状态为: j=1,s=1 →cost=1. j=2,s_prev=1 →cost=1. 但 j=2+1=3>k=2,所以只有j_prev=1的情况导致j_curr=2. 所以,最终,在i=3的curr_dp中: j=1,s=1 →cost=1(例如,路径为移动→移动→移动→停留?但停留天数总和为1?这可能不符合k=2的条件?或者,可能我的分析有误。) 或者,可能我混淆了j_prev和j_curr的计算。 例如,在i=3时,处理必须停留: 对于prev_dp中的某个j_prev和s_prev: 假设在i=2时,prev_dp[0][0] =0(移动状态,已停留0天): j_prev=0,s_prev=0. 此时,处理i=3必须停留: j_curr=0+1=1 ≤k=2. delta=1(状态从0→1). 所以,curr_dp[1][1]的值为0+1=1. 这表示,在第三天停留,总停留天数为1,移动次数为1次。这显然不满足k=2的条件,但k=2允许总停留天数为2。所以,此时总停留天数只有1,不足k=2吗? 或者,是否必须停留的天数不计入k?不,必须停留的天数必须计入总停留天数。例如,必须停留的m天必须计入k,所以当k>=m时,才能有解。 在此例中,m=1,k=2,所以总停留天数可以是1或2。但题目要求总停留天数不超过k,同时必须停留所有指定日。 因此,当总停留天数为1时,是否允许?因为必须停留的天数是1,所以总停留天数至少是1,最多是k=2. 因此,如果总停留天数为1,是否满足条件? 是的,因为题目中要求总停留天数不超过k,并且必须停留指定日。当总停留天数等于m时,也可以,只要m<=k. 因此,在测试用例中,当k=2,m=1时,可以选择总停留天数为1或2。 因此,在动态规划的最后,需要遍历j从m到k,即j>=m,因为必须停留的天数是m,所以总停留天数j必须>=m,且<=k. 在此测试用例中,m=1,k=2,所以j可以是1或2. 当i=3处理完成后,在prev_dp中有: 对于j=1,s=1 →移动次数1. 或者,例如,路径是: i=1:移动 → 状态0,j=0. i=2:移动 → 状态0,j=0. i=3:停留 → 状态1,j=1. 移动次数1. 总停留天数1(仅第3天),符合条件。此时,移动次数是1. 或者,可能存在更优的路径,例如: i=1:停留 → j=1,移动次数1. i=2:移动 → 状态0,j=1. 移动次数2. i=3:停留 → 状态1,移动次数2+1=3. 这显然不如之前的路径。 或者,另一个路径: i=1:移动 → j=0. i=2:停留 → j=1,移动次数1. i=3:停留 → j=2,移动次数1(因为从停留到停留,次数不变)。 总移动次数1,总停留天数2,符合k=2的条件。此时,移动次数为1次。 此时,在i=3的处理中,当i=2是停留(j=1,s=1),处理i=3必须停留: j_prev=1 →j_curr=2 ≤k=2. delta=0(因为前状态是1,当前也是1). 移动次数为1(来自i=2的停留) +0=1. 所以,此时,在curr_dp中,j=2,s=1的移动次数是1. 因此,最终在i=3时,curr_dp的状态可能包括: j=1,s=1 →1次. j=2,s=1 →1次. 所以,遍历j从1到2,取最小的移动次数,即1次。 所以,输出是1次,这是正确的。 这说明代码可以正确计算出最优解。 因此,代码的逻辑是正确的。 综上,这个问题的解决方法是使用动态规划,状态包括已处理的天数、已停留的天数和当前状态,转移时根据是否必须停留日来决策,最终找到满足条件下的最小移动次数。</think>要解决珠峰大本营停留规划问题,需通过动态规划确定最小移动次数。以下是具体实现步骤: ### 方法思路 1. **问题建模**:将每一天的状态(停留或移动)与已停留天数结合,用动态规划记录最小移动次数。 2. **状态定义**:定义状态`dp[i][j][s]`表示第`i`天,已停留`j`天,当前状态为`s`(0-移动,1-停留)时的最小移动次数。 3. **状态转移**: - **必须停留日**:强制选择停留状态,并增加停留天数。 - **非必须停留日**:可选择停留或移动,根据状态变化更新移动次数。 4. **初始化与优化**:使用滚动数组优化空间复杂度,仅保留前一天的状态。 ### 解决代码 ```cpp #include <vector> #include <climits> #include <algorithm> using namespace std; int minMoves(int n, int k, vector<int>& mustDays) { vector<bool> must_stay(n + 1, false); // 天数从1开始 int m = mustDays.size(); for (int day : mustDays) { if (day >= 1 && day <= n) { must_stay[day] = true; } } if (m > k) return -1; // 必须停留天数超过k,无解 const int INF = INT_MAX / 2; vector<vector<int>> prev_dp(k + 1, vector<int>(2, INF)); prev_dp[0][0] = 0; // 初始状态:已停留0天,状态为移动 for (int i = 1; i <= n; ++i) { vector<vector<int>> curr_dp(k + 1, vector<int>(2, INF)); for (int j_prev = 0; j_prev <= k; ++j_prev) { for (int s_prev : {0, 1}) { if (prev_dp[j_prev][s_prev] == INF) continue; if (must_stay[i]) { // 当前天必须停留 int j_curr = j_prev + 1; int s_curr = 1; if (j_curr > k) continue; int delta = (s_prev != s_curr) ? 1 : 0; curr_dp[j_curr][s_curr] = min(curr_dp[j_curr][s_curr], prev_dp[j_prev][s_prev] + delta); } else { // 可选择停留或移动 // 选择停留 int j_curr_stay = j_prev + 1; int s_curr_stay = 1; if (j_curr_stay <= k) { int delta = (s_prev != s_curr_stay) ? 1 : 0; curr_dp[j_curr_stay][s_curr_stay] = min(curr_dp[j_curr_stay][s_curr_stay], prev_dp[j_prev][s_prev] + delta); } // 选择移动 int j_curr_move = j_prev; int s_curr_move = 0; int delta = (s_prev != s_curr_move) ? 1 : 0; curr_dp[j_curr_move][s_curr_move] = min(curr_dp[j_curr_move][s_curr_move], prev_dp[j_prev][s_prev] + delta); } } } prev_dp = move(curr_dp); // 滚动数组优化 } // 寻找最终答案:遍历j >= m且 <=k的所有可能状态 int result = INF; for (int j = m; j <= k; ++j) { for (int s : {0, 1}) { if (prev_dp[j][s] < result) { result = prev_dp[j][s]; } } } return result == INF ? -1 : result; } ``` ### 代码解释 1. **预处理**:标记必须停留的天数,并检查是否超过`k`。 2. **动态规划初始化**:初始状态为移动,停留天数为0。 3. **状态转移**:遍历每一天,根据是否为必须停留日更新状态,计算移动次数。 4. **结果提取**:遍历所有可能的停留天数,找到最小移动次数。 此方法的时间复杂度为$O(nk)$,空间复杂度为$O(k)$,适用于中等规模的输入。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值