题意
一串数表示树高,从第一棵树开始,跳到比当前矮的不消耗体力,否则消耗一点体力,每次询问有一个步伐限制,求每次跳到最后一棵树最少耗费多少体力。
基本思路
典型的单调队列优化$dp$题,$dp$方程为
$$dp[i]=a[i]<a[min(dp[j].pos)]?dp[j]:dp[j]+1,quad (i-j)<=k$$
此时可以考虑使用一个长度为$k$的单调队列,队头表示当前消耗体力最小的树,每次新元素判断$dp[i]<dp[q[tail]]$或者 $ dp[i]=dp[q[tail]]&q[tail].height<=i.height $
此题毒瘤的一点在于卡$STL$的常数,会$TLE$两个点。
单调队列优化几点要素:
- 掐头(存区间最优值)
- 去尾(存可能最优值,若将来也一定不会最优的就去掉)
- 如果一个选手比你小,还比你强,你就可以退役了
$STL$版本
1 #include<bits/stdc++.h> 2 #define FOR(i,a,b) for(int i=a;i<b;i++) 3 #define FOR2(i,a,b) for(int i=a;i<=b;i++) 4 #define sync ios::sync_with_stdio(false);cin.tie(0) 5 #define ll long long 6 #define INF 0x7f7f7f7f; 7 #define freopenin(a) freopen(a,"r",stdin); 8 #define freopenout(a) freopen(a,"w",stdout); 9 #define MAXN 1000100 10 #define MOD 10007 11 using namespace std; 12 typedef struct{ 13 int val,pos; 14 }NODE;NODE arr[MAXN]; 15 int dp[MAXN];//单调队列优化的是dp,维护前k个最小的dp 16 int n,m; 17 int main() 18 { 19 scanf("%d",&n); 20 FOR(i,0,n)scanf("%d",&arr[i].val),arr[i].pos=i; 21 scanf("%d",&m); 22 FOR(i,0,m) 23 { 24 int k;scanf("%d",&k); 25 deque<NODE>q;//保留最高的点 26 //memset(dp,0,sizeof(dp)); 27 dp[0]=0;q.push_back(arr[0]); 28 for(int i=1;i<n;i++) 29 { 30 while(!q.empty()&&q.front().pos+k<i) q.pop_front(); 31 if(!q.empty()&&q.front().val>arr[i].val)dp[i]=dp[q.front().pos];//不消耗体力 32 else dp[i]=dp[q.front().pos]+1; 33 while(!q.empty()&&(dp[q.back().pos]>dp[i]||(dp[q.back().pos]==dp[i]&&q.back().val<=arr[i].val))) 34 q.pop_back(); 35 q.push_back(arr[i]); 36 //cout<<dp[i]<<" "; 37 } 38 printf("%d ",dp[n-1]); 39 40 } 41 return 0; 42 }
数组+指针版本
1 #include<cstdio> 2 #include<cstring> 3 #define FOR(i,a,b) for(int i=a;i<b;i++) 4 #define FOR2(i,a,b) for(int i=a;i<=b;i++) 5 #define sync ios::sync_with_stdio(false);cin.tie(0) 6 #define ll long long 7 #define INF 0x7f7f7f7f; 8 #define freopenin(a) freopen(a,"r",stdin); 9 #define freopenout(a) freopen(a,"w",stdout); 10 #define sf(a) scanf("%d",&a) 11 #define pf(a) printf("%d ",a) 12 #define sf2(a,b) scanf("%d%d",&a,&b) 13 #define MAXN 1000100 14 #define MOD 10007 15 using namespace std; 16 int arr[MAXN],q[MAXN],n,m,k,dp[MAXN]; 17 int main() 18 { 19 sf(n); 20 FOR(i,0,n)sf(arr[i]); 21 sf(m); 22 FOR(i,0,m) 23 { 24 sf(k);int head=0,tail=0; 25 memset(dp,0,sizeof(dp)); 26 dp[0]=0;q[tail]=0; 27 FOR(j,1,n) 28 {//dp 29 while(head<=tail&&(q[head]+k<j))head++;//掐头 30 if(arr[q[head]]>arr[j])dp[j]=dp[q[head]]; 31 else dp[j]=dp[q[head]]+1;//计算当前值 32 while(head<=tail&&(dp[q[tail]]>dp[j]||(dp[q[tail]]==dp[j]&&arr[q[tail]]<=arr[j]))) 33 tail--;//去尾+如果一个选手比你小,还比你强,你就可以退役了 34 q[++tail]=j; 35 //pf(dp[j]); 36 } 37 pf(dp[n-1]); 38 39 } 40 return 0; 41 }