最长上升子序列(LIS),牛客刷题

目录:

最长上升子序列(LIS)

1.模板(数据较小)

2.模板(数据较大)

牛客刷题

1. 牛客练习赛107A:如见青山

2.牛客小白月赛65A牛牛去购物

3.牛客小白月赛65B牛牛去购物

4.牛客小白月赛65C牛牛排队伍


最长上升子序列(LIS)
1.模板(数据较小)
洛谷B3637 最长上升子序列
题目描述
这是一个简单的动规板子题。给出一个由n(n≤5000) 个不超过10^6的正整数组成的序列。
请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
输入格式
第一行,一个整数n,表示序列长度。
第二行有n 个整数,表示这个序列。
输出格式
一个整数表示答案。

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 5e3+10;
int ans=0;
int a[maxn],dp[maxn];
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {   //正序枚举i的位置
        dp[i] = 1;         //每次初始化  
        for (int j = 1; j < i; j++) {   //在i前面找j的位置
            if (a[i] > a[j]) {   //如果满足条件,则第i个数可以放在第j个数后面
                dp[i] = max(dp[i], dp[j] + 1);//比较不取i位置:dp[i]
            }            //和取i位置后第i个数放在第j个数后面:dp[j]+1哪个数目大
            ans = max(ans, dp[i]);//对可能的最大值进行比较
        }
    }
    cout << ans << '\n';
    /*cout << *max_element(dp + 1, dp + 1 + n) << '\n';*/
    //*max_element(begin, end)函数返回最大元素,不加*返回地址,头文件algorithm
    //*min_element(begin, end)相同用法
}


2.(数据较大)
洛谷-AT2827最长上升子序列
 给定一个长为 n 的序列ai​,求这个序列的最长单调上升子序列长度。
(1≤ai​≤n≤10^5)

#include <iostream>
#include <algorithm>
const int maxn = 1e5 + 10;
#define inf 0x3f3f3f3f
using namespace std;
int n;
int a[maxn], b[maxn];
int low[maxn];
int mylower_bound (int r, int x) {
    int l = 1;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (low[mid] <= x)
            l = mid + 1;
        else
            r = mid - 1;
    }
    return l;
}
int main(void) {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        low[i] = inf;
    }
    int len = 1;
    low[1] = a[1];
    for (int i = 2; i <= n; ++i) {//枚举a
        if (a[i] >= low[len])//如果a[i]> 目前最长上升子序列的最优结尾,长度+1,结尾替换
            low[++len] = a[i];
        else //二分查找将该结尾放到最优的位置,替换掉前面比他大的最优结尾,成为最优结尾
            low[mylower_bound(len, a[i])] = a[i];
        /*dp[lower_bound(dp + 1, dp + len + 1, a[i]) - dp] = a[i];*/
    }
    cout << len << '\n';
    return 0;
}


1. 牛客练习赛107A:如见青山
给定n, m, 求(n!)!模m的值。其中!代表阶乘,模代表取余运算。
 思路:
 当n! >= m时,(n!)!内存在因子m, 即答案为0

#include<iostream>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
ll t,m,n, flag, ans=1;
ll dp[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin >> t >> m;
    dp[0] = 1;
    for (int i = 1; i <= maxn; i++)
    {
        dp[i] = (dp[i - 1] * i) % m;
        ans *= i;
        if (ans >= m && !flag){
            flag = i;
        }
    }
    while (t--){
        cin >> n;
        if (n >= flag) 
            cout << 0 <<'\n';
        else 
            cout << dp[dp[n]] % m << '\n';
    }
    return 0;
}


2.牛客小白月赛65A牛牛去购物
牛牛带着n 元钱去超市买东西,超市一共只有两款商品,价格为a 元的篮球和价格为b 元的足球,
牛牛想把手里的钱尽可能花光,请问牛牛最少能剩多少钱?
(1 <= n, a, b <= 1000)
 思路:
 数据较小,暴力枚举

#include<iostream>
using namespace std;
int n, a, b;
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> a >> b;
    int Min = 0x3f3f3f3f;
    for (int i = 0; i <= 1000; i++) {
        for (int j = 0; j <= 1000&& (a * i + b * j)<=n; j++) {
            Min = min(Min, n - a * i - b * j);
        }
    }
    cout << Min << '\n';
    return 0;
}


3.牛客小白月赛65B牛牛去购物
牛牛给牛妹写了一封情书(仅包含小写字母a到z),但是被牛可乐截获了,由于牛可乐也喜欢牛妹
所以往这份情书里增加了许多数字和特殊字符(也可以不加),
最后收到情书的牛妹想知道原本的情书是否包含某个字符串k。如果包含则输出 YES,否则输出 NO。
包含指k 是原本情书的子串,子串即任意连续的字符构成的子字符串,
例如对于字符串abcdefg,"abd","acg","afe"不是该字符串的子串,"
"abc","cde"是该字符串的子串。
牛可乐添加的数字和特殊字符仅包含以下这些:
0123456789 + -*| , .~!@#$%^& ()[] {}'";:?<>\/
输入描述 :
第一行输入两个正整数n, m(1≤n, m≤5×10^3) ,n 表示字符串s 的长度,m 表示字符串k 的长度。
第二行输入一个字符串s ,代表牛妹最后收到的情书。
第三行输入一个字符串k ,代表牛妹想知道原本的情书是否包含的单词。
保证s 和k 中都不包含空格。
输出描述 :
输出一行一个YES或者NO代表答案。
 思路:
 数据不是很大,同样暴力匹配即可

#include<iostream>
using namespace std;
int n, m;
string s, k;
string p = "0123456789+-*|,.~!@#$%^&()[]{}'\";:?<>\\/";
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    cin >> s >> k;
    for (int i = 0; i <= s.size(); i++) {      //枚举s
        if (s[i] == k[0]) {
            int l = i + 1, j = 1;
            while (l < s.size() && j < k.size())
            {
                bool flag = 0;
                for (int m = 0; m <= p.size(); m++) {
                    if (s[l] == p[m])
                        flag = 1;
                }
                if (s[l] == k[j])
                    l++, j++;
                else if (flag)
                    l++;
                else
                    break;
            }
            if (j == k.size()) {
                cout << "YES" << '\n';
                return 0;
            }
        }
    }
    cout << "NO" << '\n';
    return 0;
}


4. 牛客小白月赛65C牛牛排队伍
题目描述
有n 个人排队,1 号排在2 号前面,2 号排在3 号前面,...,以此类推
n−1 号排在n 号前面。在这个过程中,偶尔老师会把某个人叫走,叫走之后这个人就离开了队伍,假设本来
a 排在b 前面,b 排在c 前面,b 被叫走后a 就排在了c 前面。老师偶尔也会感到疑问,她想知道此时排在
a 号前面的同学是几号呢?
已知老师一共会有k 次操作,每次会执行下面的一种:
1. 把x 叫走。
2. 求排在x 前面的是谁。
输入描述 :
第一行输入两个正整数
n, k(1≤n, k≤10^6)n 表示排队人数,k 表示老师的操作次数。
接下来k 行,每行包含2个整数,表示一个操作,具体如下:
1 x :把x 叫走,保证此时x 一定在队伍里
2 x :输出排在x 前面的人的编号,保证此时x 一定在队伍里,如果没有人排在x 前面,输出0 。
(1≤x≤n)
输出描述:
输出包含若干行整数,即为所有操作 2 的结果。
1.(这道题卡vector,vector只能过95% 的数据!!!)
2.后移一步不能通过

#include<iostream>
using namespace std;
const int maxn = 1e6 + 10;
int n, k;
int p[maxn];//p记录每一个前面人编号
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        p[i] = i - 1;
    }
    for (int i = 1; i <= k; i++) {
        int n, x;
        cin >> n >> x;
        if (n == 2)
            cout << p[x] << '\n';
        else {
            for (int j = k; j>= x; j--) {   //向后移步
                p[j] = p[j-1];
            }
        }
    }
    return 0;
}


优化
 

#include<iostream>
using namespace std;
const int maxn = 1e6 + 10;
int n, k;
int p[maxn];//p记录每一个前面人编号
int Next[maxn];//记录后一个人编号
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		p[i] = i - 1;
		Next[i] = i + 1;
	}
	for (int i = 1; i <= k; i++) {
		int n, x;
		cin >> n >> x;
		if (n == 2)
			cout << p[x] << '\n';
		else {
			int a = p[x];
			int b = Next[x];
			p[b] = p[x];
			Next[a] = Next[x];
		}
	}
	return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值