2022-02-12每日刷题打卡

114 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

2022-02-12每日刷题打卡

AcWing——算法基础

895. 最长上升子序列 - AcWing题库

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数 N。

第二行包含 N 个整数,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N≤1000
−109≤数列中的数≤109

输入样例:

7
3 1 2 1 8 5 6

输出样例:

4

准备一个数组f,设状态为:f[i]是以第i个数为尾的所能达到的最长序列长度。

先把数都存入一个数组里,然后从第二个位置开始遍历数组(f[0]就可以直接设置为1了,因为数组的开头不管怎么样反正就一个数而已,长度肯定是0),每次遍历到一个新位置时,回过头去遍历之前的所有位置(下标为j),如果那些位置上的数有小于当前遍历到的第i个数的,那么就设置f[i]=max(f[i],f[j]+1),如果那个数小于我们现在的数,说明我们的数可以接在那个数的后面,那么长度自然就是以那个数为尾的最长序列的长度+1,即f[j]+1。在此过程中也要维护最大值res,res记录的是最长的序列长度。最后返回res即可。

#include<iostream>
using namespace std;

const int N=1010;
int f[N],w[N];

int main()
{
    int n,res=0;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>w[i];
        f[i]=1;
    }
    for(int i=1;i<n;i++)
    {
        for(int j=i;j>=0;j--)
        {
            if(w[i]>w[j])
            {
                f[i]=max(f[i],f[j]+1);
                res=max(res,f[i]);
            }
        }
    }
    cout<<res<<endl;
    return 0;
}

896. 最长上升子序列 II - AcWing题库

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数 N。

第二行包含 N 个整数,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N≤100000
−109≤数列中的数≤109

输入样例:

7
3 1 2 1 8 5 6

输出样例:

4

这题是上一题的难度加大版。数据长度从1000变成了100000,如果我们用上一题的写法会超时。

但我们可以想办法去优化上一题的解法,使其能通过这一题。上一题,我们每遍历一个数,就要回过头去遍历之前走过的数,以找到一个能让自己接在后面的数,并把自己的长度改成接在自己后面数的长度+1。就是说,我们要从之前找过的地方找一个能使我们长度最大的数,既然是找东西,我们就可以用二分查找的方式来查,但二分查找的前提是有序性,我们的数组显然是无序的,f数组也是无序的,那么现在问题就是把f数组变得有序。我们可以修改一下f数组对应的状态:f[i]的状态表示是,最长长度为i的序列,其尾部的数最小可以是x。就是说,我们根据长度来存数,比如这里的样例,第一个数字3,它的长度就是1,所以f[1]=3,到了第二个数1的时候,因为f数组里没有比他小的数,所以他无法接到其它数的后面,但是他比长度为1的数:3要小,所以它可以取代这个位置,此时f[1]=1,到第3个数字2,只用找f里比他小的数就好,即接到f[1]的后面,这样f[2]=2……这样下来,f数组就会变得有序,比较如果我这个数比其它数大,那我的长度肯定要比他们长,那自然我就会排在前面了。我们每次遍历到一个新的数时,就可以用二分查找,在f数组里找一个尽可能比自己大的数,然后这个数就可以存在那个数长度+1的位置上。最后只要知道f数组最多存了多大长度,就可以知道这个数组所能得到的最大上升序列长度为多少了。

#include<iostream>
using namespace std;

const int N=100010;
int f[N],w[N];

int main()
{
    int n,len=0;
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>w[i];
    
    f[0]=-2e9;
    for(int i=0;i<n;i++)
    {
        int l=0,r=len;
        while(l<r)
        {
            int mid=l+r+1>>1;
            if(f[mid]<w[i])l=mid;
            else r=mid-1;
        }
        len=max(len,r+1);
        f[r+1]=w[i];
    }
    cout<<len<<endl;
    return 0;
}

蓝桥杯——蓝桥云课

数字三角形 - 蓝桥云课 (lanqiao.cn)

题目描述

图片描述

上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。

路径上的每一步只能从一个数走到下一层和它最近的左边的那个数或者右 边的那个数。此外,向左下走的次数与向右下走的次数相差不能超过 1。

输入描述

输入的第一行包含一个整数 N (1≤N≤100),表示三角形的行数。

下面的 N 行给出数字三角形。数字三角形上的数都是 0 至 100 之间的整数。

输出描述

输出一个整数,表示答案。

输入输出样例

示例

输入

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

输出

27

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

这题和898. 数字三角形 - AcWing题库差不多(我在昨天的打卡里写过,看不懂这题的可以回去看看),就是多了个左右移动次数相差不超过1的条件。其实没有多复杂,还是基础的数字三角形的解法。对于这个左右移动次数不超过1来说,我们从上往下移动,最后肯定会落在中间位置,如果n是奇数,那就是落在正中间(假如n是5,那么就会落在第三个位置上),如果n是偶数,就是落在靠中间的两个数(假如n是6,就会落在第3和第4个位置上)我们只要根据n的奇偶性,在这几个位置上找最大值就行。

#include <iostream>
using namespace std;

const int N = 110;
int f[N][N], w[N][N];

int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j <= i; j++)
        {
            cin >> w[i][j];
        }
    }
    f[0][0] = w[0][0];
    for (int i = 1; i < n; i++)
    {
        for (int j = 0; j <= i; j++)
        {
            if (j == 0)f[i][j] = f[i - 1][j] + w[i][j];
            else if (j == i)f[i][j] = f[i - 1][j - 1] + w[i][j];
            else f[i][j] = max(f[i - 1][j], f[i - 1][j - 1]) + w[i][j];
        }
    }
    int res = 0;
    if (n % 2 == 0)
    {
        res = max(f[n-1][n / 2], f[n-1][n / 2 - 1]);
    }
    else res = f[n-1][n / 2];
    cout << res << endl;
    return 0;
}