P1220 关路灯
简要题意:
有 \(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\),在去中间纯属是浪费时间。
如何设置状态转移方程?如下:
每个方程的形式都是 \(\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; //其余情况赋极大值