[luoguP1220]关路灯

原题

像这种每一阶段有两种决策的题,千万不能像我一样,翻译题目条件朴素地解答。

这样做的后果就是 你发现需要同时通过回溯获得两个值,很棘手。

比如说 设你当前所处的位置为阶段,以向左关和向右关作为每个阶段的决策

这样在每个阶段就会有走路步数、消耗功、关灯数量等三个状态。

如果以关掉该灯所用时间来计算消耗的功,我们就需要从最后一个关掉的灯倒着推,

因为后面所用时间你是无从知晓的,只能从上一个阶段转移过来,

最终设定边界条件。

看起来好像能行,但问题是步数和消耗的功都需要从上一个阶段转移过来,

而且步数还受消耗的功的影响:

消耗的功需要选择上一阶段 消耗功的 最小值,

选择上一阶段 消耗功的 最小值 又会影响转移来的步数的多少……

更要命的是 , 你很难判断某个路灯你是否关过,

这意味着你需要使用vis数组记录,不满足可使用记搜的条件

这条路上充满坎坷,我们选择放弃


(图源网络,侵删)

(不过用这个思路打暴搜是个不错的选择)

换另一条路走。

联想区间dp的模板题奶牛零食

和这个问题很相似,每次都有两种选择,每次选择都伴随着“价值”的 变化

不同的是,“关路灯”这道题,“价值”,即每个路灯消耗的功与选择次数没有很确定的关系

我们可以反其道而行之,每次计算到达这个路灯之前所有未关路灯在这段时间内消耗的功

为了方便表示,我们把存储的顺序进行改变,

从左往中间某处 是老张左端的路灯,最左边距离老张最近,从左往右第二个距离老张第二近,以此类推。

从右往中间某处 是老张右端的路灯,最右边距离老张最近,从右往左第二个距离老张第二近,以此类推。

由于老张一开始就把自己所处位置的路灯关上了,所以老张的路灯我们可以选择不存。

于是我们发现可以套用区间dp过题,

用left和right顺带表示[1,n-1]内[left,right]之外的 路灯均已被关闭

设f[left][right][pos]为从left到right路灯未关且当前关掉pos处路灯时已消耗的功,

sum[left][right]表示剩余未关灯的 总功率,

light[x].pos表示编号为x的路灯的实际位置,

light[x].w表示编号为x的路灯的功率,

count_l表示左端路灯最远处编号,

count_r表示右端路灯最远处编号,

n表示路灯总数,

于是有:

f[left][right][pos] = min( f[left+1][right][left]+sum[left][right]abs(light[left].pos-light[pos].pos),
f[left][right-1][right]+sum[left][right]
abs(light[right].pos-light[pos].pos) )
(left<=count_l&&right>=count_r)
f[left][right][pos] = f[left+1][right][left]+sum[left][right]abs(light[left].pos-light[pos].pos
(left<=count_l&&right<count_r)
f[left][right][pos] = f[left][right-1][right]+sum[left][right]
abs(light[right].pos-light[pos].pos
(right>=count_r&&left>count_l)

临界状态:f[x][x][y] = light[x].w*abs(light[x].pos-light[y].pos) (xcount_l || xcount_r ,1<=y<=n-1)

时间复杂度O(n^3)

  • questions left

输出关灯路径

  • 实现过程中的小问题

不要把一个将要被读入而未被读入的变量的值赋给另一个变量

注意保存顺序与设想一致

边界条件不一定为0

原文地址:https://www.cnblogs.com/StarOnTheWay/p/10884891.html