【算法札记】练习 | Week5

目录

  • [73. 笨小猴(模拟)​](#73. 笨小猴(模拟))
  • [74. 主持人调度(一)(排序)​](#74. 主持人调度(一)(排序))
  • [75. 分割等和子集(动态规划 - 01背包)​](#75. 分割等和子集(动态规划 - 01背包))
  • [76. 小红的ABC(字符串 + 找规律)](#76. 小红的ABC(字符串 + 找规律))
  • [77. 不相邻取数(动态规划 - 线性 dp)​](#77. 不相邻取数(动态规划 - 线性 dp))
  • [78. 空调遥控(滑动窗口)​](#78. 空调遥控(滑动窗口))
  • [79. kotori和气球(组合数学)](#79. kotori和气球(组合数学))
  • [80. 走迷宫(BFS)](#80. 走迷宫(BFS))
  • [81. 主持人调度(二)(贪心 + 优先级队列)](#81. 主持人调度(二)(贪心 + 优先级队列))
  • [82. 游游的重组偶数(数学)​](#82. 游游的重组偶数(数学))
  • [83. 体操队形(DFS + 枚举)](#83. 体操队形(DFS + 枚举))
  • [84. 二叉树中的最大路径和(递归)​](#84. 二叉树中的最大路径和(递归))
  • [85. 排序子序列(模拟/dp)](#85. 排序子序列(模拟/dp))
  • [86. 削减整数(贪心)](#86. 削减整数(贪心))
  • [87. 最长上升子序列(二)(贪心 + 二分)](#87. 最长上升子序列(二)(贪心 + 二分))
  • [88. 爱吃素(数学)](#88. 爱吃素(数学))
  • [89. 相差不超过k的最多数(滑动窗口)​](#89. 相差不超过k的最多数(滑动窗口))
  • [90. 最长公共子序列(一)(动态规划 - LCS)](#90. 最长公共子序列(一)(动态规划 - LCS))

73. 笨小猴(模拟)​

https://www.nowcoder.com/practice/17865bc2a75c4944a872ef709958c56e?tpId=290&tqId=39933&ru=/exam/oj

根据题意模拟

cpp 复制代码
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
string s;
bool isprim(int n) 
{ // 判断 n 是否是质数​
    if (n < 2) return false;
    for (int i = 2; i <= sqrt(n); i++) {
        if (n % i == 0) return false;
    }
    return true;
}

int main() 
{
    cin >> s;
    int hash[26] = { 0 };
    for (auto ch : s) 
    {
        hash[ch - 'a']++;
    }
    int minn = 1000, maxn = 0;
    for (int i = 0; i < 26; i++) 
    {
        if (hash[i]) 
        {
            minn = min(minn, hash[i]);
            maxn = max(maxn, hash[i]);
        }
    }

    if (isprim(maxn - minn)) 
    {
        cout << "Lucky Word" << endl;
        cout << maxn - minn << endl;
    } 
    else 
    {
        cout << "No Answer" << endl;
        cout << 0 << endl;
    }
    return 0;
}

74. 主持人调度(一)(排序)​

https://www.nowcoder.com/practice/e160b104354649b69600803184094adb?tpId=196&tqId=40514&ru=/exam/oj

区间问题技巧:按照左端点排序或者按照右端点排序

左端点排序后,我们仅需考虑后续区间是否能与前一个区间重叠即可。

cpp 复制代码
class Solution {
  public:
    bool hostschedule(vector<vector<int> >& schedule) 
    {
        sort(schedule.begin(), schedule.end(), [](vector<int>& v1, vector<int>& v2)->bool{
            return v1[0] < v2[0];
        });
        for (int i = 1; i < schedule.size(); i++) 
        {
            if (schedule[i][0] < schedule[i - 1][1]) return false;
        }
        return true;
    }
};

75. 分割等和子集(动态规划 - 01背包)​

https://www.nowcoder.com/practice/65ade309fa4d4067a9add749721bfdc0?tpId=230&tqId=40433&ru=/exam/oj

01 背包问题:原问题转换成,从 n 个数中选,总和恰好为 sum / 2,能否挑选出来。

cpp 复制代码
#include <iostream>
using namespace std;
const int N = 510, M = 510 * 110 / 2;
int n;
int arr[N];
int dp[N][M];
int main() {
    cin >> n;
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        cin >> arr[i];
        sum += arr[i];
    }

    if (sum % 2 == 1) cout << "false" << endl;
    else {
        sum /= 2;
        dp[0][0] = true;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= sum; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j >= arr[i]) {
                    dp[i][j] = dp[i][j] || dp[i - 1][j - arr[i]];
                }
            }
        }
        if (dp[n][sum]) cout << "true" << endl;
        else cout << "false" << endl;
    }
    return 0;
}

76. 小红的ABC(字符串 + 找规律)

https://ac.nowcoder.com/acm/problem/230720

由于题目要找的是最短的回文子串,并且只有三个字母 a b c,因此最短的回文子串的长度要么是2,要么是 3。​

因此,我们仅需枚举所有的二元组以及三元组就好了。

cpp 复制代码
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
    string s;
    cin >> s;
    int ans = -1;
    for(int i = 0; i < s.size(); i++)
    {
        int l = i-1, r = i+1;
        if(l >= 0 && r < s.size() && s[l] == s[r]) ans = 3;
 
        l = i, r = i + 1;
        if(r < s.size() && s[l] == s[r]) ans = 2;
    }
     
    cout << ans << endl;
    return 0;
}

77. 不相邻取数(动态规划 - 线性 dp)​

https://www.nowcoder.com/practice/a2be806a0e5747a088670f5dc62cfa1e?tpId=230&tqId=39763&ru=/exam/oj

打家劫舍题型

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
    int n;
    cin >> n;
    vector<int> nums(n);
    for(int i = 0; i < n; i++)
    {
        cin >> nums[i];
    }
    vector<long long> dp1(n), dp2(n);
    // dp1不取 dp2取
    dp1[0] = 0;
    dp2[0] = nums[0];
    for(int i = 1; i < n; i++)
    {
        dp1[i] = max(dp1[i-1], dp2[i-1]);
        dp2[i] = max(dp1[i-1]+nums[i], dp2[i-1]);
    }  
    cout << max(dp1[n-1], dp2[n-1]) << endl;
    return 0;
}

78. 空调遥控(滑动窗口)​

https://ac.nowcoder.com/acm/problem/229310

滑动窗口:

维护窗口内最大值与最小值的差在 2*p 之间。

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
    long long n, p;
    cin >> n >> p;
    vector<long long> nums(n);
    for(int i = 0; i < n; i++)
    {
        cin >> nums[i];
    }
    sort(nums.begin(), nums.end());
    int ans = 0;
    int l = 0, r = 0;
    while(r < n)
    {
        while(nums[r] - nums[l] > 2*p)
        {
            l++;
        }
        ans = max(ans, r-l+1);
        r++;
    }
    cout << ans << endl;
    return 0;
}

79. kotori和气球(组合数学)

https://ac.nowcoder.com/acm/problem/50039

排列组合问题,第一个位置可以放n种,之后的每个位置都能放n-1种。结果等于n与 m-1个n - 1的乘积。

cpp 复制代码
#include <iostream>
using namespace std;
 
int main()
{
    int n, m;
    cin >> n >> m;
    int ans = n;
     
    for(int i = 1; i < m; i++)
    {
        ans = ans * (n-1);
        ans %= 109;
    }
    cout << ans << endl;
    return 0;
}

80. 走迷宫(BFS)

https://www.nowcoder.com/practice/e88b41dc6e764b2893bc4221777ffe64?tpId=308&tqId=40477&ru=/exam/oj

经典bfs问题。

cpp 复制代码
#include <iostream>
#include <string>
#include <queue>
#include <vector>
using namespace std;
 
int main()
{
    int n, m;
    int x1, y1, x2, y2;
    cin >> n >> m;
    cin >> x1 >> y1 >> x2 >> y2;
    vector<vector<char>> grid(n, vector<char>(m));
    for(int i = 0; i < n; i++)
    {
        string s;
        cin >> s;
        for(int j = 0; j < m; j++)
            grid[i][j] = s[j];
    }
 
    vector<vector<bool>> vis(n, vector<bool>(m, 0));
    int dx[4] = {0, 0, 1, -1}; 
    int dy[4] = {1, -1, 0, 0};
    queue<pair<int, int>> q;
    q.push({x1-1, y1-1});
    vis[x1-1][y1-1] = true;
    int ans = 1;
    while(!q.empty())
    {
        int sz = q.size();
        while(sz--)
        {
            auto [a, b] = q.front();
            q.pop();
            for(int k = 0; k < 4; k++)
            {
                int x = a + dx[k];
                int y = b + dy[k];
                if(x >= 0 && x < n && y >= 0 && y < m && grid[x][y] == '.' && !vis[x][y])
                {
                    if(x == x2-1 && y == y2-1)
                    {
                        cout << ans << endl;
                        return 0;
                    }
                    q.push({x, y});
                    vis[x][y] = true;
                }
            }
        }
        ans++;
    }
    cout << -1 << endl;
    return 0;
}

81. 主持人调度(二)(贪心 + 优先级队列)

https://www.nowcoder.com/practice/4edf6e6d01554870a12f218c94e8a299?tpId=196&tqId=37562&ru=/exam/oj

原数组按照左端点排序。搞个小根堆:

  • 先把第一个区间的右端点加入到堆中;
  • 遍历后面的区间,分情况讨论:
    • 如果左端点大于等于堆顶元素,能接在后面,干掉堆顶,然后把这个区间的右端点加入堆;
    • 否则的话,只能再来一个人,只把这个区间的右端点加入堆。
  • 最后堆的大小就是需要的人数。
cpp 复制代码
class Solution 
{
public:
    int minmumNumberOfHost(int n, vector<vector<int> >& startEnd) 
    {
        sort(startEnd.begin(), startEnd.end(), [](vector<int>& v1, vector<int>& v2){
            return v1[0] < v2[0];
        });
        priority_queue<int, vector<int>, greater<int>> pq;
        pq.push(startEnd[0][1]);
        for(int i = 1; i < n; i++)
        {
            int top = pq.top();
            if(startEnd[i][0] >= top)
            {
                pq.pop();
            }
            pq.push(startEnd[i][1]);
        }
        return pq.size();
    }
};

82. 游游的重组偶数(数学)​

https://www.nowcoder.com/questionTerminal/d1ac7f15d5dc40b39a7d6cb11a01407e

从后往前遍历每一位数,把第一个遇到的偶数和个位交换就行了。这种方式也不会出现前导零

cpp 复制代码
#include <algorithm>
#include <iostream>
using namespace std;
 
void solve()
{
    string s;
    cin >> s;
    for(int i = s.size()-1; i >= 0; i--)
    {
        if((s[i]-'0') % 2 == 0)
        {
            swap(s[s.size()-1], s[i]);
            cout << s << endl;
            return;
        }
    }
    cout << -1 << endl;
    return;
}
 
int main()
{
    int q;
    cin >> q;
    while(q--)
    {
        solve();
    }
    return 0;
}

83. 体操队形(DFS + 枚举)

https://ac.nowcoder.com/acm/problem/229386

dfs,重点是画出决策树和剪枝:从前往后,如果在放置一个人的时候,他的诉求已经被使用,说明这条路径是不合法的。

cpp 复制代码
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int n;
bool vis[11];
int ans;

void dfs(vector<int>& v, int pos)
{
    if(pos == n+1)
    {
        ans++;
        return;
    }
    for(int i = 1; i <= n; i++)
    {
        if(vis[i] == false)
        {
            int hope = v[i];
            if(vis[hope])
            {
                return;
            }
            vis[i] = true;
            dfs(v, pos+1);
            vis[i] = false;
        }
    }
}

int main()
{
    cin >> n;
    vector<int> v(n+1);
    memset(vis, 0, sizeof vis);
    ans = 0;
    for(int i = 1; i <= n; i++)
    {
        cin >> v[i];
    }
    dfs(v,1);
    cout << ans << endl;
    return 0;
}

84. 二叉树中的最大路径和(递归)​

https://www.nowcoder.com/practice/da785ea0f64b442488c125b441a4ba4a?tpId=196&tqId=37050&ru=/exam/oj

树形dp:​

  • 左子树收集:以左子树为起点的最大单链和;
  • 右子树收集:以右子树为起点的最大单链和;
  • 根节点要做的事情:整合左右子树的信息,得到经过根节点的最大路径和;
  • 向上返回:以根节点为起点的最大单链和
cpp 复制代码
class Solution {
public:
    int ret = INT_MIN;
    // 找当前子树中的最大值单链
    int dfs(TreeNode* root)
    {
        if(root == nullptr)
            return 0;
         
        int l = max(dfs(root->left), 0);
        int r = max(dfs(root->right), 0);
        ret = max(ret, l + r + root->val);
        return max(l, r) + root->val;
    }
 
    int maxPathSum(TreeNode* root)
    {
       dfs(root);
       return ret;
    }
};

85. 排序子序列(模拟/dp)

https://www.nowcoder.com/questionTerminal/2d3f6ddd82da445d804c95db22dcc471

可以根据题意模拟。也可以用dp。

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

int main()
{
    int n;
    cin >> n;
    vector<long long> nums(n);
    for(int i = 0; i < n; i++)
    {
        cin >> nums[i];
    }
    vector<vector<int>> dp(n, vector<int>(2, 1));
    //dp[i][0]升序结束, dp[i][1]降序结束
    for(int i = 1; i < n; i++)
    {
        if(nums[i] > nums[i-1])
        {
            dp[i][0] = min(dp[i-1][0], dp[i-1][1] + 1);
            dp[i][1] = min(dp[i-1][0], dp[i-1][1]) + 1;
        }
        else if(nums[i] < nums[i-1])
        {
            dp[i][0] = min(dp[i-1][0], dp[i-1][1]) + 1;
            dp[i][1] = min(dp[i-1][1], dp[i-1][0] + 1);
        }
        else
        {
            dp[i][0] = dp[i-1][0];
            dp[i][1] = dp[i-1][1];
        }
 
    }
    cout << min(dp[n-1][0], dp[n-1][1]) << endl;
    return 0;
}

86. 削减整数(贪心)

https://ac.nowcoder.com/acm/problem/219038

想要最少的次数,应该尽可能将减数翻倍。但是为了确保能恰好减为零,必须保证剩余被减数是翻倍后的减数的倍数。

cpp 复制代码
#include <iostream>
using namespace std;
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        int h;
        cin >> h;
        
        int ret = 0;
        int x = 1;
        while(h)
        {
            h -= x;
            ret++;
            if(h % (2*x) == 0)
            {
                x *= 2;
            }
        }
        cout << ret << endl;
    }
    return 0;
}

87. 最长上升子序列(二)(贪心 + 二分)

https://www.nowcoder.com/practice/4af96fa010c44638a7e112abf65f7237?tpId=196&tqId=39285&ru=/exam/oj

这是一道非常经典的题型。一般有两种做法,动态规划O(n2),贪心+二分O(nlogn)。这道题的数据量代表要求用后者解决。

cpp 复制代码
#include <vector>
class Solution {
public:

    int LIS(vector<int>& a) 
    {
        int n = a.size();
        vector<int> dp(n+1, 0);
        // dp[i]为长度为i的最小末尾
        int pos = 0;
        for(int x : a)
        {
            if(pos == 0 || x > dp[pos])
            {
                pos++;
                dp[pos] = x;
            }
            else 
            {
                int l = 1, r = pos;
                while(l < r)
                {
                    int m = l + (r-l)/2;
                    if(dp[m] >= x)
                    {
                        r = m;
                    }
                    else 
                    {
                        l = m + 1;
                    }
                }
                dp[l] = x;
            }
        }
        return pos;
    }
};

88. 爱吃素(数学)

https://ac.nowcoder.com/acm/problem/227310

素数的定义:大于1的自然数,除了1和自身,没有别的因数。

不需要真的相乘ab,分类讨论即可。

cpp 复制代码
#include <iostream>
#include <cmath>
using namespace std;
 
bool isprime(long long x)
{
    if(x == 1)
        return false;
     
    for(long long i = 2; i <= sqrt(x); i++)
    {
        if(x % i == 0)
        {
            return false;
        }
    }
    return true;
}
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        long long a, b;
        cin >> a >> b;
        if((a == 1 && isprime(b)) || (b == 1 && isprime(a)))
        {
            cout << "YES" << endl;
        }
        else
        {
            cout << "NO" << endl;
        }
    }
    return 0;
}

89. 相差不超过k的最多数(滑动窗口)​

https://www.nowcoder.com/practice/562630ca90ac40ce89443c91060574c6?tpId=308&tqId=40490&ru=/exam/oj

排序后,使用滑动窗口

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

int main()
{
    int n, k;
    cin >> n >> k;
    vector<int> nums(n);
    for(int i = 0; i < n; i++)
    {
        cin >> nums[i];
    }
    sort(nums.begin(), nums.end());
    int ans = 0;
    int l = 0, r = 0;
    while(r < n)
    {
        while(nums[r] - nums[l] > k)
        {
            l++;
        }
        ans = max(ans, r-l+1);
        r++;
    }
    cout << ans << endl;
    return 0;
}

90. 最长公共子序列(一)(动态规划 - LCS)

https://www.nowcoder.com/practice/672ab5e541c64e4b9d11f66011059498?tpId=230&tqId=39761&ru=/exam/oj

经典两个字符串之间的 dp 问题

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

int main()
{
    string s1, s2;
    int n, m;
    cin >> n >> m;
    cin >> s1 >> s2;
    vector<vector<int>> dp(n, vector<int>(m));
    dp[0][0] = s1[0]==s2[0]? 1: 0;
    for(int i = 1; i < n; i++)
        dp[i][0] = s1[i]==s2[0]? 1: dp[i-1][0];

    for(int j = 1; j < m; j++)
        dp[0][j] = s1[0]==s2[j]? 1: dp[0][j-1];

    for(int i = 1; i < n; i++)
        for(int j = 1; j < m; j++)
            dp[i][j] = s1[i]==s2[j]? dp[i-1][j-1]+1: max(dp[i-1][j], dp[i][j-1]);

    cout << dp[n-1][m-1] << endl;
    return 0;
}
相关推荐
RFID舜识物联网1 小时前
耐高温RFID:让喷涂线从“数据断点”走向“全链贯通”
大数据·人工智能·嵌入式硬件·物联网·汽车
人月神话-Lee1 小时前
【图像处理】图像直方图——从“频率分布“到“智能决策“
图像处理·人工智能·ios·ai编程·swift
z小猫不吃鱼1 小时前
10 大语言模型基本术语总结:参数、Token、Context、Logits、Temperature
人工智能·语言模型·自然语言处理
咕咕咕估1 小时前
codexx 找到你丢失的会话
人工智能
隔窗听雨眠1 小时前
基于大模型API的活动策划辅助系统设计与实现
人工智能·大模型
源分享1 小时前
什么是人工智能?非常详细
人工智能
启途AI1 小时前
当营销话术超越产品实体:GEO市场的诚信挑战
大数据·人工智能·搜索引擎·ai·chatgpt
一块谈1 小时前
自治工厂
人工智能
2601_958352901 小时前
A-59双麦模块实战:打造面对面双人独立拾音与实时翻译系统,全双工无串扰
人工智能·嵌入式硬件·语音识别·回音消除·音频处理模块