洛谷-P3572 [POI2014]PTA-Little Bird-单调队列优化dp

题意

一串数表示树高,从第一棵树开始,跳到比当前矮的不消耗体力,否则消耗一点体力,每次询问有一个步伐限制,求每次跳到最后一棵树最少耗费多少体力。

基本思路

典型的单调队列优化$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$两个点。

单调队列优化几点要素:

  1. 掐头(存区间最优值)
  2. 去尾(存可能最优值,若将来也一定不会最优的就去掉)
  3. 如果一个选手比你小,还比你强,你就可以退役了 

$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\n",dp[n-1]);
39         
40     }
41     return 0;
42 } 
View Code

数组+指针版本

 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\n",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 }
View Code

猜你喜欢

转载自www.cnblogs.com/tldr/p/11333328.html