钻石收集者&是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(详情可以去绑定的资源内看图解)

相关推荐
plus4s1 小时前
2月20日(88-90题)
算法
仰泳的熊猫1 小时前
题目1529:蓝桥杯算法提高VIP-摆花
数据结构·c++·算法·蓝桥杯
陆嵩1 小时前
CG 方法(共轭梯度)的数学推导及其算法
算法·cg·共轭梯度·lanczos·arnoldi·正交化·gram-schmidt
twilight_4691 小时前
机器学习与模式识别——SVM
算法·机器学习·支持向量机
小糯米6012 小时前
C++ 树
数据结构·c++·算法
liliangcsdn2 小时前
IMPALA强化学习算法的学习和解读
学习·算法
再难也得平2 小时前
[LeetCode刷题]283.移动零(通俗易懂的java题解)
java·算法·leetcode
不想看见4042 小时前
House Robber 基本动态规划:一维--力扣101算法题解笔记
笔记·算法·leetcode·代理模式
掘根2 小时前
【C++STL】红黑树(RBTree)
数据结构·c++·算法