琪露诺 双端队列优化转移方程
众所周知,琪露诺是以笨蛋闻名的冰之妖精。
题目:https://www.luogu.org/problemnew/show/P1725
显然,是一道DP题(很恶心显然二字,现在来恶心你们)
状态转移是dp[i]=max(dp[k])+a[i];
跳到当前位置的最大值是前面能跳到到这里的所有位置的最大值加上当前位置的冰冻值;
为什么要用双端队列呢(因为爽啊)
这个很裸了已经,滑动窗口的思想。就是纯DP是会T的,所以我们寻找最大值时要优化时间;
如果当前的值比队列中的值大,就把队列中的已有元素弹出(因为我们是从前往后遍历的,一个位置在前且值小的元素是不会做出任何贡献的);
代码
#include<cstdio> #include<cstring> #include<deque> #include<algorithm> using namespace std; const int maxn=200100; struct node { int val;//DP最大值 int num;// }; int n,a[maxn],l,r; int dp[maxn]; deque <node> s; int main() { scanf("%d%d%d",&n,&l,&r); for(int i=0;i<=n;i++) { scanf("%d",a+i); } int tail=0;dp[tail]=0; node x; for(int i=l;i<=n;i++)//从0开始转移向l处 { while(s.size()&&dp[tail]>=s.back().val) { s.pop_back(); }//无用值弹出 x.num=tail;x.val=dp[tail]; s.push_back(x);//加入新值 if(tail-s.front().num>=(r-l+1)) s.pop_front();//队列的长度 dp[i]=s.front().val+a[i];//此时队列最前面的是前面能到达i的最大值 tail++; } int ans=-maxn; for(int i=n-r+1;i<=n;i++) { ans=max(ans,dp[i]); } printf("%d",ans); return 0; }