9.19日志

今天打了一下算法巅峰赛,五题有一道简单题少拿10分,原因未明,最后一题不会100分骗20分,之后查了一下发现是博弈论dp,做法是记忆化搜索,十分逆天,别人都说简单但是抽到的博弈论dp我之前甚至都没听过,拿到这题的时候完全没往dp的方向想过,尝试了sg函数和bfs都不行,还是菜,练好dp吧,今天学习了单调队列优化dp,这类题往往是结合前缀和一起考察,利用滑动窗口的办法求出区间内的最值,从而将复杂度从n*m优化到n,晚上还学习了英语,学到了一套英语单词记忆方法,也大致确定好了英语的学习规划,之后就是执行了,以后csdn也准备写一道题写一篇题解,放在最后写往往会为了早点上床而为了写而写没什么意义。

算法板块

例题1.135. 最大子序和 - AcWing题库

首先做一个前缀和,之后发现我们要找的子序列就是ai - a[k](k在[i-m ~ i - 1]之间),为使结果最大我们就要使得a[k]尽量小,那么问题就转换成了我们要对于ai我们要找[i - m ~ i - 1]中最小的a[j],那么就会想到单调队列使得以线性的复杂度解决问题。

注意:

1.答案可能是负数,因此res初始化为负无穷

2.一开始需要往队列中推入0(实际上应该推入m-1个0,但是考虑到滑动窗口的性质,只保留1个0也是一样的效果),若不添加0则相当于对于ai(i < m)而言我们只考虑了[1 ~ i - 1]的最值.

3.res的转移应该在将i加入队列之前,因为我们要找的是[i - m ~ i - 1]之间的最值,若在添加i之后转移则相当于找的是[i - m ~ i]的最值

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

typedef long long ll;    
const int N = 3e5 + 10;

ll a[N],q[N];
int n,m;

int main()
{
    cin>>n>>m;
    
    for(int i = 1 ; i <= n ; i++)
    {
        cin>>a[i];
        a[i] += a[i - 1];
    }
    
    int tt = -1,hh = 0;
    q[++tt] = 0;
    ll res = -1e18;
    
    for(int i = 1 ; i <= n ; i++)
    {
        while(hh <= tt && q[hh] < i - m)
        {
            hh++;
        }
        while(hh <= tt && a[q[tt]] >= a[i])
        {
            tt--;
        }
		res = max(res , a[i] - a[q[hh]]);
        q[++tt] = i;
    }
    
    cout<<res;
}

例题2.P1725 琪露诺 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

和上一道题有异曲同工之妙,不同点在于上一题是通过相邻的区间转移i点,这题则是通过[i - r , i - l]区间去更新dp[i],我们需要维护的是一个长度为r - l + 1的滑动窗口来求区间最大值,通过已经求到的区间来更新dp[i + l],本题需要注意的是数组别开小,因为我们的答案存在于[n , n + r]之间,并且初始化也很重要,因为求的是最大值所以dp理所当然初始化为负无穷,但是[0 , l - 1]这一段区间是到不了的应该初始化为0(对结果不影响)最重要的是对[l , r]的初始化不能忽略,因为[l , r]中的位置必定是第一次跳到的所以dp[i] = a[i](i >= l && i <= r),我们之后的递推也是由[l , r]这一段开始的。

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

typedef long long ll;    
const int N = 5e5 + 10;

int n,l,r;
ll a[N],dp[N];
int q[N];

int main()
{
    cin>>n>>l>>r;
    for(int i = 0 ; i <= n ; i++)
    {
        cin>>a[i];
    }
    
    for(int i = l ; i <= n + r ; i++)
    {
        if(i >= l && i <= r)
        {
            dp[i] = a[i];
        }else
        {
            dp[i] = -1e18;
        }   
    }

    int tt = -1,hh = 0,k = r - l + 1;
    for(int i = l ; i + l <= n + r ; i++)
    {
        while(hh <= tt && q[hh] <= i - k)
        {
            hh++;
        }

        while(hh <= tt && dp[q[tt]] <= dp[i])
        {
            tt--;
        }
        q[++tt] = i;
        dp[i + l] = max(dp[i + l] , dp[q[hh]] + a[i + l]);
    }

    ll maxn = -1e18;
    for(int i = n ; i <= n + r ; i++)
    {
        maxn = max(maxn , dp[i]);
    }

    cout<<maxn;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值