UVA1169题解
作者:互联网
一道很有意思的dp。
首先,这显然是一个线性 dp,状态显然是现在的垃圾编号和从哪个垃圾编号转移得到,在这里我们约定都是由。
于是我们就可以得到一个状态转移方程:
\[dp[i]=\min\limits_{1\le j \le n}\left\{dp[j]+dis(i,j)\right\},\sum^{i}_{k=j}c_k\le C \]其中 \(dis(i,j)\) 表示从编号 \(i\) 的垃圾收集到 编号为 \(j\) 的垃圾加上从原点到两个垃圾所需的路程。
显然,转移有 \(n\) 个,而求出 \(i\) 到 \(j\) 的路径需要 \(O(n)\) 的时间,于是时间复杂度就是 \(O(n^3)\)。
显然刚才的时间复杂度过不去,而容易想到我们可以用前缀和搞一下,把复杂度变成 \(O(n^2)\)。
接着我们发现有很多状态是不合法的,因为我们需要满足 \(\sum^{i}_{k=j}c_k\le C\) 的条件。
接着我们来看看现在我们的状态转移方程:
\[dp[i]=\min\limits_{1\le j\le n}\left\{d[j]+pre[i]-pre[j+1]+x[i]+y[i]+x[j+1]+y[j+1]\right\} \]然后我们设
\[A[i]=pre[i]+x[i]+y[i] \]\[B[i]=d[i]-pre[i+1]+x[i+1]+y[i+1] \]接着,我们的状态转移方程就可以写成这样
\[dp[i]=\min\limits_{1\le j\le n}\left\{A[i]+B[j]\right\} \]还有,我们观察到 \(c_i\) 都大于 \(0\),显然 \(i-j\) 越大,\(\sum^{i}_{j=k}c_k\) 也会越大。
这时如果有一个 \(j\) 值的 \(B[j]\) 不仅小,而且离 \(i\) 远,那我们一定没有考虑这个决策的必要,这让人自然想到单调队列。
于是我们就可以优化成 \(O(n)\) 了,最后注意一下输出格式,每组数据中有空行。
Code:
#include<iostream>
using namespace std;
const int maxn=1e5+5;
inline int abs(int x) {
return x>0?x:-x;
}
int x[maxn],y[maxn],w[maxn],d[maxn],prew[maxn],prep[maxn];
int q[maxn],l,r;
inline int f(int i){return d[i]-prep[i+1]+x[i+1]+y[i+1];}//简化B[i]
int main() {
int t;
cin>>t;
while(t--) {
int c,n;
cin>>c>>n;
for(int i=1; i<=n; ++i) {
cin>>x[i]>>y[i]>>w[i];
prep[i]=prep[i-1]+abs(x[i]-x[i-1])+abs(y[i]-y[i-1]);
prew[i]=prew[i-1]+w[i];
}
l=0,r=1;
for(int i=1; i<=n; ++i) {
while(l<r&&prew[i]-prew[q[l]]>c)l++;
int j=q[l];
d[i]=d[j]+prep[i]-prep[j+1]+x[i]+y[i]+x[j+1]+y[j+1];
while(l<r&&f(q[r-1])>=f(i))r--;
q[r++]=i;
}
cout<<d[n]<<endl;
if(t>0)cout<<endl;//注意输出格式
}
return 0;
}
标签:pre,le,UVA1169,题解,int,maxn,prep,dp 来源: https://www.cnblogs.com/for-hyf/p/14797592.html