像这种每一阶段有两种决策的题,千万不能像我一样,翻译题目条件朴素地解答。
这样做的后果就是 你发现需要同时通过回溯获得两个值,很棘手。
比如说 设你当前所处的位置为阶段,以向左关和向右关作为每个阶段的决策
这样在每个阶段就会有走路步数、消耗功、关灯数量等三个状态。
如果以关掉该灯所用时间来计算消耗的功,我们就需要从最后一个关掉的灯倒着推,
因为后面所用时间你是无从知晓的,只能从上一个阶段转移过来,
最终设定边界条件。
看起来好像能行,但问题是步数和消耗的功都需要从上一个阶段转移过来,
而且步数还受消耗的功的影响:
消耗的功需要选择上一阶段 消耗功的 最小值,
选择上一阶段 消耗功的 最小值 又会影响转移来的步数的多少……
更要命的是 , 你很难判断某个路灯你是否关过,
这意味着你需要使用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