「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;
}
posted @ 2024-01-31 11:44  inlinexhx  阅读(2)  评论(0编辑  收藏  举报