「POI 2014」PTA-Little Bird (单调队列优化dp)
本来是昨天的题,但是由于今天不想做题所以来水一篇
这道题算是比较板子的单调队列优化dp了,不知道为什么评蓝。
分析
很像琪露诺的多测版,只是计算答案有略微不同。
设 \(dp_{i,j}\) 为第 \(i\) 只鸟经过第 \(j\) 颗树的最小劳累值。
容易得出转移式:
\[\textstyle dp_{i,j}= \min\{dp_{i,p} + d_p\le d_j\} p\in [j-k_i,j-1]
\]
这个做法是 \(\mathcal{O}(n^2q)\) 的,显然无法通过此题。
我们发现可以将最小值用单调队列处理,时间复杂度优化到 \(\mathcal{O}(nq)\) ,可以通过本题。
由于 \(n\) 只鸟之间不会相互影响,可以压掉一维,但是需要在每次循环时初始化一下数组。
具体细节看代码
Code
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#define ll long long
#define re register
#define inf 0x3f3f3f3f
#define F(i,a,b) for(re int i=(a);i<=(b);i++)
#define f(i,a,b) for(re int i=(a);i>=(b);i--)
inline int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x/10) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);printt(x);}
void put(int x){print(x);puts("");}
const int N=1e6+10;
const int M=30;
int n,m;
int d[N],k[N];
int dp[N];
int q[N];
int head,tail;
void init()
{
memset(dp,0,sizeof dp);
head=1,tail=0;
q[++tail]=1;
}
int main()
{
n=read();
F(i,1,n) d[i]=read();
m=read();
F(i,1,m) k[i]=read();
F(i,1,m)
{
init();//初始化
F(j,2,n)
{
while(head<=tail&&q[head]+k[i]<j) head++;
dp[j]=dp[q[head]]+(d[q[head]]<=d[j]);//计算答案
while(head<=tail&&(dp[q[tail]]+(d[q[tail]]<=d[j])>dp[j])) tail--;
q[++tail]=j;//加入队列
}
put(dp[n]);
}
return 0;
}