P11114 [ROI 2024] 小推车 (Day 1) 题解
题目注意
是从1~n的顺序服务乘客,并且每一次必须要到第一个没有被处理的人
关键思路
首先需要想到,我们的瓶子是动态调整的,也就是说如果我们当前有空瓶子,就可以变成当前处理的乘客所需的颜色
再进行进一步思考,我们定义\(w_i\)表示到\(i\)所需要的瓶子数量,不难发现,\(w_i\)是定值,也就意味着我们到达任意一个位置,需要的瓶子数量是确定的
状态设计
接下来设计\(f_i\),表示处理到\(i\)位置所需的最小移动次数,定义\(v_i\)表示从\(i\)位置返回补充点并返回到下个服务位置所需要的最小移动次数,那么:
\(f_i = min(f_j + v_j + i - j - 1)\)
理解一下这个式子,这个式子实际上表示的是从在\(j\)位置前往补充,并到下一个服务位置,然后一直处理到\(i\)
那么这就需要在\(j\)位置所提供的(\(w_j+\)能够补充的瓶子数量$ \ge w_i\(),定义\)q_i\(表示在\)i\(位置所剩下的空瓶数目,则\)w_i+q_i\(就表示在\)i\(位置前往补充所能得到的瓶子总数(所使用的\)w_i\(+未使用的\)q_i\(),则若\)w_j+q_i < w_i\(,也就表示在\)j\(位置进行补充所获得的瓶子总数依然无法满足\)i$位置的需求,那么显然是不能转移的
汇总一下,也就是一下两条:
- \(f_i = min(f_j + v_j + i - j - 1)\)
- \(w_j+q_j \ge w_i\)
初始的状态也可以讲下,\(q_0 = m,f_0 = 0,w_0 = 0\)
优化DP
如果只是强行DP,时间复杂度是\(O(n^2)\)的,不能接受,考虑优化
观察到\(w_i+q_i\)不降(因为瓶子总数不可能减少),因此可以使用单调队列优化DP,这个部分比较板,不会的可以看Code
Code
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 1e6 + 10;
deque<int> d;
int n,m,k,p;
int c;
int a[N];
int con[N];
LL w[N],q[N],val[N],f[N];
LL calc(int u){
return f[u] + val[u] - u;
}
int main()
{
IOS;
cin >> n >> m >> k >> p;
cin >> c;
for(int i=1;i<=n;i++)
cin >> a[i];
q[0] = m;
for(int i=1;i<=n;i++){//预处理出w_i,q_i,val_i
w[i] = w[i-1];
q[i] = q[i-1];
if((con[a[i]] % p) == 0){
w[i] ++ ;
q[i] -- ;
}
con[a[i]] ++ ;
if((con[a[i]] % p) == 0){
q[i] ++ ;
}
if(c == 1) val[i] = 2 * n - 2 * i + 1;
if(c == 2) val[i] = 2 * i + 1;
if(c == 3) val[i] = min(2 * n - 2 * i + 1,2 * i + 1);
}
val[0] = 1;
d.push_back(0);
for(int i=1;i<=n;i++){
f[i] = calc(d.front()) + i - 1;
while(!d.empty() && calc(d.back()) >= calc(i))//如果队尾的元素没有当前位置优,直接弹出
d.pop_back();
d.push_back(i);
while(!d.empty() && d.front() < i+1 && w[d.front()] + q[d.front()] < w[i+1]){//如果队头的元素无法转移的限制,直接弹出
d.pop_front();
}
}
cout << f[n] + 1;
return 0;
}

浙公网安备 33010602011771号