钻石收集者&是7倍数的最长子序列&Zuma

本篇是寒假的第二弹,虽然每天都有练习算法,但是假期的话想要挤出时间写博客还是比较紧张,还有Linux要学;今天就写了一道算法题,剩下的时间拿出来写博客吧!话不多说上成果!!!

(1)钻石收集者

以以前写的代码展开详细描述与解释,并附上题目

cpp 复制代码
#include<iostream>
#include<algorithm>
using namespace std;

const int N=5e4+10;
int n,k;
int a[N];
int f[N],g[N];

int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+1+n);
    for(int left=1,right=1;right<=n;right++)
    {
        while(a[right]-a[left]>k) left++;
        f[right]=max(f[right-1],right-left+1);
    }
    for(int left=n,right=n;left>=1;left--)
    {
        while(a[right]-a[left]>k) right--;
        g[left]=max(g[left+1],right-left+1);
    }
    int ret=0;
    for(int i=2;i<=n;i++)
    {
        ret=max(ret,f[i-1]+g[i]);
    }
    cout<<ret<<endl;
    return 0;
}

这是一道在洛谷上写的题目,所以使用全局的变量更加方便,根据输入要求,我直接把数组设置的足够大,那么就不用考虑越界的问题

实现本题的逻辑主要在于滑动窗口思想

  • 首先,需要将数组从大到小依次排序起来这样,方便后面遍历数组
  • 数组f[N]:从左往右统计一次,正向
  • 数组g[N]:从右往左统计一次,反向(这两个数组是实现滑动窗口思想的底层容器)
  • f[right]:表示以right为结尾的数段中,所能达到的最大长度
  • g[left]:表示以left为开头的数段中,所能达到的最大长度
  • f[i-1]+g[i]:表示以i位置分割的左右两部分,左右结合起来能达到的最大长度
  • ret:因为最大长度可能存在任意一个位置,所以需要暴搜,找寻最终的答案

(2)是7倍数的最长子序列

以以前写的代码展开详细描述与解释,并附上题目

cpp 复制代码
#include<iostream>
#include<cstring>
using namespace std;

int n;
int id[10];

int main()
{
    cin>>n;
    memset(id,-1,sizeof id);
    id[0]=0;
    int sum=0,ret=0;
    for(int i=1;i<=n;i++)
    {
        int x;cin>>x;
        sum=(sum+x)%7;
        if(id[sum]!=-1) ret=max(ret,i-id[sum]);
        else id[sum]=i;
    }
    cout<<ret<<endl;
    return 0;
}

这是一道在洛谷上写的题目,所以使用全局的变量更加方便,根据输入要求,我直接把数组设置的足够大,那么就不用考虑越界的问题

实现本题的逻辑主要在于前缀和+哈希表+同余定理

  • 数组id:本道题使用数组代替哈希表,这样可以节省空间,因为任意一个数%7后,数值都在0~6之间,所以我们数组不用开太大,就开10即可
  • 初始化:一开始把数组都初始化为-1,这样就可以清楚这个位置我们是否已经遍历过与否,此处值仍为-1,就是还没有遍历过;不为-1,就是已经存进去了一个结点下标

我们需要特地将id[0]这个点初始化为0,因为如若整个序列就是7的倍数的话,那么计算 的时候,就是拿此时id[0]内的下标进行计算

  • 实现逻辑:因为是要找到最长的子序列,那么我们只要存%7的0~6这几个数第一次出现的下标即可;后续若重复出现,就可以计算此时的长度,与ret比较
  • 同余定理:文字不方便描述,可以到绑定的资源内看图解,更好理解

(3)Zuma

以今天写的代码展开详细描述与解释,并附上题目

cpp 复制代码
#include<iostream>
#include<cstring>
using namespace std;

const int N=521;
int n;
int a[N];
int f[N][N];

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    memset(f,0x3f,sizeof f);
    for(int i=1;i<=n;i++)
        f[i][i]=1;
    for(int i=1;i+1<=n;i++)
    {
        int j=i+1;
        if(a[i]==a[j]) f[i][j]=1;
        else f[i][j]=2;
    }

    for(int len=3;len<=n;len++)
    {
        for(int i=1;i+len-1<=n;i++)
        {
            int j=i+len-1;
            for(int k=i;k<j;k++)
            {
                //[i,k][k+1][j]
                f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
            }
            if(a[i]==a[j]) f[i][j]=min(f[i][j],f[i+1][j-1]);
        }
    }
    cout<<f[1][n]<<endl;
    return 0;
}

这是一道在洛谷上写的题目,所以使用全局的变量更加方便,根据输入要求,我直接把数组设置的足够大,那么就不用考虑越界的问题

实现本题的逻辑主要在于区间dp

  • 状态表示f[i][j]:把区间[i,j]内的宝石全部移走,所需要的最少步骤
  • 状态表示:

①基于分割点k考虑:f[i][k]+f[k+1][j]

②基于左右端点考虑:当a[i]==a[j]时,f[i+1][j-1](容易遗漏,则会错过最优解)

  • 本题难点初始化:

①先将所有的位置初始化为无穷大

②再把所有的基础长度为1,2的区间初始化

③注意f[i][j]初始化时,因为会用到f[i+1][j-1]初始化,但是如若不进行第二部,那么两个位置都是无穷大,就无法取出最小值,进行后续的dp(详情可以去绑定的资源内看图解)

相关推荐
菜鸟‍6 分钟前
【论文学习】Disco:基于邻接感知协同着色的密集重叠细胞实例分割方法
人工智能·学习·算法
牧天白衣.12 分钟前
力扣215.数组中的第K个最大元素
算法·leetcode
cxr82819 分钟前
控制理论基础
人工智能·算法
平平淡淡才是true32 分钟前
偏序关系、哈斯图、最长链长度、最长链条数
算法
小钊(求职中)36 分钟前
算法知识、常用方法总结
java·算法·排序算法·力扣
paeamecium41 分钟前
【PAT甲级真题】- Talent and Virtue (25)
数据结构·c++·算法·pat
Mr_Xuhhh1 小时前
蓝桥杯复习清单真题(C++版本)
c++·算法·蓝桥杯
tankeven1 小时前
HJ163 时津风的资源收集
c++·算法
Boop_wu1 小时前
[Java 算法] 动态规划(4)
数据结构·算法·leetcode
旖-旎1 小时前
分治(计算右侧小于当前元素的个数)(7)
c++·学习·算法·leetcode·排序算法·归并排序