洛谷P1725 琪露诺 (单调队列/堆优化DP)
显然的DP题.....
对于位置i,它由i-r~i-l的位置转移过来,容易得到方程 dp[i]=dp[i]+max(dp[i−r],...,dp[i−l])。
第一种:n2的暴力,只能拿部分分。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=2e5+5,inf=0x3f3f3f3f; 4 int n,l,r,a[N],dp[N],ans=-inf; 5 6 int main(){ 7 scanf("%d%d%d",&n,&l,&r); 8 for(int i=0;i<=n;i++) scanf("%d",&a[i]); 9 memset(dp,-0x3f,sizeof(dp)); 10 dp[0]=0; 11 for(int i=l;i<=n+r;i++){ 12 for(int j=max(i-r,0);j<=i-l;j++){ 13 dp[i]=max(dp[i],dp[j]+a[i]); 14 } 15 if(i>n) ans=max(ans,dp[i]); 16 } 17 cout<<ans; 18 }
注意细节:i从l到n+r枚举,i-r可能为负导致越界,所以取max(i-r,0);
第二种:单调队列优化,max(dp[i−r],...,dp[i−l])就是找这个区间中的最大值,类似于滑动窗口的思想,建立一个单减的队列。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=2e5+5; 4 int dp[N],q[N],a[N],n,l,r,ans=-0x3f3f3f3f; 5 6 int main(){ 7 scanf("%d%d%d",&n,&l,&r); 8 for(int i=0;i<=n;i++) scanf("%d",&a[i]); 9 memset(dp,-0x3f,sizeof(dp)); 10 dp[0]=0; 11 int h=1,t=0; 12 for(int i=l;i<=n;i++){ 13 while(h<=t && dp[q[t]]<=dp[i-l]) t--; 14 q[++t]=i-l; 15 while(h<=t && q[h]<i-r) h++; 16 dp[i]=dp[q[h]]+a[i]; 17 } 18 for(int i=n-r+1;i<=n;i++) 19 ans=max(ans,dp[i]); 20 cout<<ans<<endl; 21 }
第三种:堆优化(大根堆),思想和上一种差不多,都是维护区间最大值,(个人感觉堆优化的代码要简单一小小小些)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=2e5+5,inf=0x3f3f3f3f; 4 int n,l,r,dp[N],a[N],ans=-inf; 5 struct node{ 6 int p,val; 7 friend bool operator < (node a,node b){ 8 return a.val<b.val;//大根堆 9 } 10 }; 11 priority_queue<node> q; 12 int main(){ 13 scanf("%d%d%d",&n,&l,&r); 14 memset(dp,-0x3f,sizeof(dp)); 15 for(int i=0;i<=n;i++) scanf("%d",&a[i]); 16 dp[0]=0; 17 for(int i=l;i<=n;i++){ 18 q.push((node){i-l,dp[i-l]}); 19 while(q.top().p<i-r) q.pop(); 20 dp[i]=q.top().val+a[i]; 21 } 22 for(int i=n-r+1;i<=n;i++) 23 ans=max(ans,dp[i]); 24 cout<<ans; 25 }
对于这种题,要写出DP方程,观察式子看能不能使用数据结构来优化,可能优化方式不止一种。