P3572 [POI2014] PTA-Little Bird

原题链接

题解

首先,考虑接下来往哪颗树飞是很困难的,因为当前的决策会影响之后的决策

但是如果考虑到达当前树从哪里飞过来就比较好了,因为无后效性

接着我们可以暴力做法,遍历每棵树从前 k 个树飞过来的值,然后取最小的那个,但是这样显然会超时,所以我们优化一下

有哪些值得被优化的地方?--有很多无用决策点

什么是无用决策点?请看下文

对于两棵相邻的树,i,i+1,设 dp[i] 为到达该树的最小疲劳值

  • 如果 dp[i]dp[i+1] 同时 h[i]h[i+1]

那么我们可以抛弃转移点 i,因为能从 i 飞过来的选择,也一定能从 i+1 飞过来,且不劣

  • 如果 dp[i]>dp[i+1] 同时 h[i]>h[i+1]

那么我们可以抛弃转移点 i,因为能从 i 飞过来的选择,也一定能从 i+1 飞过来,且不劣

解释:如果后面有 h[i+1]<h[j]h[i] ,那么两个转移点的效果是一样的,否则 i+1 更优

  • 如果 dp[i]==dp[i+1] 同时 h[i]>h[i+1]

都保留,因为对于 [i+2,i+k] 的树来说,从 i 转移过来更优,但是 i+k+1 或许需要转移点 i+1

  • 如果 dp[i]<dp[i+1]

都保留,因为对于 [i+2,i+k] 的树来说,从 i 转移过来更优,但是 i+k+1 或许需要转移点 i+1

因此,我们可以维护一个队列,该队列特性为从队首到队尾,其下标递增,疲劳值递增,疲劳值相同的点树的高度递减

code

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int dp[1000006]={0},h[1000006]={0};

void solve()
{
    int n;
    cin>>n;

    for(int i=1;i<=n;i++) cin>>h[i];

    int q;
    cin>>q;

    while(q--)
    {
        int k;
        cin>>k;

        deque<int> pre;

        pre.push_back(1);
        dp[1]=0;

        for(int i=2;i<=n;i++)
        {
            if(pre.front()<i-k) pre.pop_front();

            dp[i]=dp[pre.front()]+(h[i]>=h[pre.front()]);

            while(pre.size()&&(dp[pre.back()]>dp[i]||dp[pre.back()]==dp[i]&&h[pre.back()]<=h[i])) pre.pop_back();

            pre.push_back(i);

            //cout<<dp[i]<<'\n';
        }

        cout<<dp[n]<<'\n';
        //cout<<'\n';
    }
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--) solve();
    return 0;
}


posted @   纯粹的  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示