P1220 关路灯
网页链接
P1220 关路灯
题目描述
某一村庄在一条路线上安装了
n
n
n 盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少)。老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯。
为了给村里节省电费,老张记录下了每盏路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能够最节省电。他每天都是在天亮时首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯。开始他以为先算一下左边路灯的总功率再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头来关掉另一边的路灯,而事实并非如此,因为在关的过程中适当地调头有可能会更省一些。
现在已知老张走的速度为
1
m
/
s
1m/s
1m/s,每个路灯的位置(是一个整数,即距路线起点的距离,单位:
m
m
m)、功率(
W
W
W),老张关灯所用的时间很短而可以忽略不计。
请你为老张编一程序来安排关灯的顺序,使从老张开始关灯时刻算起所有灯消耗电最少(灯关掉后便不再消耗电了)。
输入格式
第一行是两个数字
n
n
n(表示路灯的总数)和
c
c
c(老张所处位置的路灯号);
接下来
n
n
n 行,每行两个数据,表示第
1
1
1 盏到第
n
n
n 盏路灯的位置和功率。数据保证路灯位置单调递增。
输出格式
一个数据,即最少的功耗(单位:
J
J
J,
1
J
=
1
W
×
s
1J=1W\\times s
1J=1W×s)。
输入输出样例 #1
输入 #1
5 3
2 10
3 20
5 20
6 30
8 10
输出 #1
270
说明/提示
样例解释
此时关灯顺序为 3 4 2 1 5。
数据范围
1
≤
n
≤
50
1\\le n\\le50
1≤n≤50,
1
≤
c
≤
n
1\\le c\\le n
1≤c≤n,
1
≤
W
i
≤
100
1\\le W_i \\le 100
1≤Wi≤100,
1
≤
路灯位置
≤
100
1 \\leq \\text{路灯位置} \\leq 100
1≤路灯位置≤100
解题思路
解题思路
本题是区间动态规划的经典问题。老张关灯的过程可以看作是在一条直线上不断扩展已关灯的连续区间,因此可以用区间 DP 来求解最小功耗。
1. 问题等价转化
- 路灯按位置升序排列,老张从第
c
c
c 盏灯出发,每次关掉一盏灯。由于他只会走向相邻的未关路灯,因此任意时刻已经关掉的路灯必然构成一个连续的区间[
i
,
j
]
[i, j]
[i,j]。 - 老张关灯后可能停在区间的左端点
i
i
i 或右端点j
j
j,因此需要两个状态来区分。 - 功耗 = 时间 × 功率。老张行走速度为
1
m/s
1\\,\\text{m/s}
1m/s,所以行走时间等于行走距离。在行走过程中,所有尚未关掉的路灯仍在耗电。因此,每次移动的功耗 = 移动距离 × 当前未关路灯的总功率。
2. 动态规划设计
- 状态定义:
- F[i][j][0]:关掉区间
[
i
,
j
]
[i, j]
[i,j] 的所有路灯后,老张停在左端点i
i
i 时的最小总功耗。 - F[i][j][1]:关掉区间
[
i
,
j
]
[i, j]
[i,j] 的所有路灯后,老张停在右端点j
j
j 时的最小总功耗。
- F[i][j][0]:关掉区间
- 前缀和:S[i] 表示前
i
i
i 盏灯的总功率。关掉区间[
i
,
j
]
[i, j]
[i,j] 后,未关路灯包括[
1
,
i
−
1
]
[1, i-1]
[1,i−1] 和[
j
+
1
,
n
]
[j+1, n]
[j+1,n],其总功率为:rem
(
i
,
j
)
=
S
[
i
−
1
]
+
S
[
n
]
−
S
[
j
]
\\text{rem}(i, j) = S[i-1] + S[n] – S[j]
rem(i,j)=S[i−1]+S[n]−S[j] 但在转移过程中,需要注意移动时目标灯尚未关掉,因此计算功耗时未关灯的总功率应包含目标灯。例如从i
+
1
i+1
i+1 走到i
i
i 时,此时i
i
i 还未关,所以未关灯总功率为S
[
i
]
+
S
[
n
]
−
S
[
j
]
S[i] + S[n] – S[j]
S[i]+S[n]−S[j](因为[
i
+
1
,
j
]
[i+1, j]
[i+1,j] 已关)。 - 转移方程(区间长度 len 从
2
2
2 到n
n
n):- 对于区间
[
i
,
j
]
[i, j]
[i,j](j
=
i
+
l
e
n
−
1
j = i + len – 1
j=i+len−1),考虑最后一步:- 若最后停在
i
i
i,则上一步可能停在i
+
1
i+1
i+1(从i
+
1
i+1
i+1 向左走到i
i
i)或停在j
j
j(从j
j
j 一路向左走到i
i
i):F
[
i
]
[
j
]
[
0
]
=
min
{
F
[
i
+
1
]
[
j
]
[
0
]
+
(
X
[
i
+
1
]
−
X
[
i
]
)
×
(
S
[
i
]
+
S
[
n
]
−
S
[
j
]
)
F
[
i
+
1
]
[
j
]
[
1
]
+
(
X
[
j
]
−
X
[
i
]
)
×
(
S
[
i
]
+
S
[
n
]
−
S
[
j
]
)
F[i][j][0] = \\min \\begin{cases} F[i+1][j][0] + (X[i+1] – X[i]) \\times (S[i] + S[n] – S[j]) \\\\ F[i+1][j][1] + (X[j] – X[i]) \\times (S[i] + S[n] – S[j]) \\end{cases}
F[i][j][0]=min{F[i+1][j][0]+(X[i+1]−X[i])×(S[i]+S[n]−S[j])F[i+1][j][1]+(X[j]−X[i])×(S[i]+S[n]−S[j]) - 若最后停在
j
j
j,则上一步可能停在j
−
1
j-1
j−1(从j
−
1
j-1
j−1 向右走到j
j
j)或停在i
i
i(从i
i
i 一路向右走到j
j
j):F
[
i
]
[
j
]
[
1
]
=
min
{
F
[
i
]
[
j
−
1
]
[
0
]
+
(
X
[
j
]
−
X
[
i
]
)
×
(
S
[
i
−
1
]
+
S
[
n
]
−
S
[
j
−
1
]
)
F
[
i
]
[
j
−
1
]
[
1
]
+
(
X
[
j
]
−
X
[
j
−
1
]
)
×
(
S
[
i
−
1
]
+
S
[
n
]
−
S
[
j
−
1
]
)
F[i][j][1] = \\min \\begin{cases} F[i][j-1][0] + (X[j] – X[i]) \\times (S[i-1] + S[n] – S[j-1]) \\\\ F[i][j-1][1] + (X[j] – X[j-1]) \\times (S[i-1] + S[n] – S[j-1]) \\end{cases}
F[i][j][1]=min{F[i][j−1][0]+(X[j]−X[i])×(S[i−1]+S[n]−S[j−1])F[i][j−1][1]+(X[j]−X[j−1])×(S[i−1]+S[n]−S[j−1]) 注意:在计算 F[i][j][0] 时,未关灯总功率为S
[
i
]
+
S
[
n
]
−
S
[
j
]
S[i] + S[n] – S[j]
S[i]+S[n]−S[j];在计算 F[i][j][1] 时,未关灯总功率为S
[
i
−
1
]
+
S
[
n
]
−
S
[
j
−
1
]
S[i-1] + S[n] – S[j-1]
S[i−1]+S[n]−S[j−1]。这是因为移动的目标灯不同,目标灯在到达前尚未关掉。
- 若最后停在
- 对于区间
- 边界条件:
- 初始状态:F[c][c][0] = F[c][c][1] = 0(只有第
c
c
c 盏灯被关,老张就在该位置)。 - 其他所有 F 初始化为一个极大值(代码中用 memset(F, 127, sizeof(F)) 实现,即将每个字节设为 0x7f,对 long long 而言是一个很大的数)。
- 初始状态:F[c][c][0] = F[c][c][1] = 0(只有第
- 最终答案:min(F[1][n][0], F[1][n][1]),即关掉所有灯后,无论老张停在左端还是右端的最小功耗。
3. 复杂度分析
- 时间复杂度:状态数为
O
(
n
2
)
O(n^2)
O(n2),每个状态转移O
(
1
)
O(1)
O(1),总时间复杂度O
(
n
2
)
O(n^2)
O(n2)。n
≤
50
n \\le 50
n≤50,运算量极小。 - 空间复杂度:需要存储三维 DP 数组 F[60][60][2] 以及位置、功率、前缀和数组,空间复杂度
O
(
n
2
)
O(n^2)
O(n2),完全可接受。
总结
将关灯过程建模为区间扩展,用两个状态分别表示老张停在区间左端或右端。转移时考虑从相邻区间扩展而来,并乘以当前未关路灯的总功率(即剩余灯仍在耗电)。通过前缀和快速计算未关灯总功率,实现
O
(
1
)
O(1)
O(1) 转移。该方法思路清晰,是区间 DP 在资源调度类问题中的典型应用。
代码内容
#include <bits/stdc++.h>
using namespace std;
#define endl '\\n'
typedef long long ll;
typedef unsigned long long ull;
typedef vector<vector<ll>> vvt;
typedef pair<ll,ll> pll;
const ll N=1e3+10;
const ll INF=1e18;
const ll M=1e6+10;
const ll mod=1e9+7;
const ll MAXM=60;
ll X[MAXM],Y[MAXM],S[MAXM],n,c;
ll F[MAXM][MAXM][2];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
scanf("%lld%lld",&n,&c);
memset(F,127,sizeof(F));
for(ll i=1;i<=n;i++)
{
scanf("%lld%lld",&X[i],&Y[i]);
S[i]=S[i–1]+Y[i];
}
F[c][c][0]=F[c][c][1]=0;
for(ll len=2;len<=n;len++)
{
for(ll i=1;i+len–1<=n;i++)
{
ll j=i+len–1;
F[i][j][0]=min(F[i+1][j][0]+(X[i+1]–X[i])*(S[i]+S[n]–S[j]),F[i+1][j][1]+(X[j]–X[i])*(S[i]+S[n]–S[j]));
F[i][j][1]=min(F[i][j–1][0]+(X[j]–X[i])*(S[i–1]+S[n]–S[j–1]),F[i][j–1][1]+(X[j]–X[j–1])*(S[i–1]+S[n]–S[j–1]));
}
}
ll ans=min(F[1][n][0],F[1][n][1]);
printf("%lld",ans);
return 0;
}
实现100%通过率-171主机测评](https://www.171host.com/wp-content/uploads/2026/10/20261026130223-6adf4f5f34184-220x80.gif)



![打卡信奥刷题(3652)用C++实现信奥题 P12076 [OOI 2025] Cute Subsequences-171主机测评](https://www.171host.com/wp-content/uploads/2026/10/20261026055333-6adeeaddd0c48-220x150.png)

