P1220 关路灯

CSDN同步

原题链接

简要题意:

\(n\) 个路灯,你一开始在第 \(c\) 个路灯的位置。每个路灯都有 \(L_i\) (距离)。每过 \(1s\),第 \(i\) 个路灯就会产生 \(P_i\) 的消耗,你可以走 \(1\) 个单位距离。你可以花 \(0s\) 的时间关掉路灯,关掉路灯之后路灯不会再有消耗。你需要求出:最小的消耗是多少?

\(1 \leq n \leq 50\),是不是很简单?

我们首先已经有了:

\(\mathcal{O}(2^n \cdot n)\) 的算法,大力枚举关路灯的顺序,计算消耗即可。

显然 \(n \leq 50\) 是个误导,我们直接把数据范围改成:

\(1 \leq n \leq 3 \times 10^3\).

这下仔细一考虑,典型的 区间 \(\text{dp}\) 啊!

\(f_{i,j}\) 表示 \([i,j]\) 的答案,\(s\) 表示 \(P\) 的前缀和(老套路了)。

但是你会发现问题来了:你不知道 \(f_{i,j}\) 你是从 \(i+1 \rightarrow i\) 还是 \(j-1 \rightarrow j\) 还是 \(i \rightarrow j\) 还是 \(j \rightarrow i\).

你不知道你现在的位置在 \(i\) 还是 \(j\),无法计算啊!

所以,用 \(f_{i,j,0}\) 表示 \([i,j]\) 且当前在 \(i\) 的答案,\(f_{i,j,1}\) 是在 \(1\) 的答案。

诚然你会说:可能不在 \(i,j\),在区间中间。

不对。因为你最后关掉的路灯一定是 \(i\) 或者 \(j\),在去中间纯属是浪费时间。

如何设置状态转移方程?如下:

\[\begin{cases} f_{i,j,0} = \min(f_{i+1,j,0} + (L_{i+1} - L_i) \cdot (s_n - s_j + s_i) , f_{i+1,j,1} + (L_j - L_i) \cdot (s_n - s_j + s_i)) \\ f_{i,j,1} = \min(f_{i,j-1,0} + (L_j - L_i) \cdot (s_n - s_{j-1} + s_{i-1}) , f_{i,j-1,1} + (L_j - L_{j-1}) \cdot (s_n - s_{j-1} +s_{i-1})) \\ \end{cases}\]

每个方程的形式都是 \(\text{当前状态} = \text{前一个状态} + \text{所需关灯时间} \cdot \text{区间之外其它灯 1s 的消耗}\).

最后答案即 \(\min(f_{1,n,0} , f_{1,n,1})\).

时间复杂度:\(\mathcal{O}(n^2)\).

实际得分:\(100pts\).

for(i=1;i<=n;i++) s[i] = s[i-1] + P[i];
for(l=1;l<=n;l++)
for(int i=1,j;(j=i+l-1)<=n;i++) 
	if(i<=c && c<=j) { // c 必须在区间里
		f[i][j][0]=min(f[i+1][j][0] + (L[i+1] - L[i]) * (s[n] - s[j] + s[i]),f[i+1][j][1] + (L[j] - L[i]) * (s[n] - s[j] + s[i]));
		f[i][j][1]=min(f[i][j-1][0] + (L[j] - L[i]) * (s[n] - s[j-1] + s[i-1]),f[i][j-1][1] + (L[j] - L[j-1]) * (s[n] - s[j-1] + s[i-1]));
	}else f[i][j][0] = f[i][j][1] = INF; //其余情况赋极大值
posted @ 2020-07-17 21:25  bifanwen  阅读(214)  评论(0编辑  收藏  举报