笔试强训题(1)

1. Day01

1.1 数字统计(数学 + 模拟)

  1. 题目链接BC153 [NOIP2010]数字统计
  2. 题目描述:

  1. 解法:
    • 算法思路:循环提取末尾,然后干掉末尾。
  2. C++ 算法代码:
#include <iostream>
using namespace std;

int main() 
{
    int left = 0;
    int right = 0;
    cin >> left >> right;
    
    int nums = 0;
    for(int i = left; i <= right; i++)
    {
        int tmp = i;
        while(tmp != 0)
        {
            if(tmp % 10 == 2)
            {
                nums++;
            }

            tmp /= 10;
        }
    }

    cout << nums;

    return 0;
}

1.2 两个数组的交集(哈希)

  1. 题目链接NC313 两个数组的交集
  2. 题目描述:

  1. 解法:
    • 将其中⼀个数组丢进哈希表中;
    • 遍历另⼀个数组的时候,在哈希表中看看就好了;
  2. C++ 算法代码:
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) 
    {
        bool hash[1010] = { 0 };
        vector<int> ret;
        for (auto x : nums1) 
        {
            hash[x] = true;
        }

        for (auto x : nums2) 
        {
            if(hash[x]) 
            {
                ret.push_back(x);
                hash[x] = false;
            }
        }

        return ret;
    }

	// vector<int> intersection(vector<int>& nums1, vector<int>& nums2)
	// {
	//     unordered_set<int> hash1(nums1.begin(), nums1.end());
	//     unordered_set<int> hash2(nums2.begin(), nums2.end());
	
	//     vector<int> nums;
	//     for(auto iter = hash1.begin(); iter != hash1.end(); ++iter)
	//     {
	//         if(hash2.count(*iter) != 0)
	//         {
	//             nums.push_back(*iter);
	//         }
	//     }
	
	//     return nums;
	// }
};

1.3 点击消除(栈)

  1. 题目链接AB5 点击消除
  2. 题目描述:

  1. 解法:
    • 算法思路:用栈来模拟消除的过程。
  2. C++ 算法代码:
#include <iostream>
#include <stack>
using namespace std;

int main() 
{
    string str;
    cin >> str;

    string ret;
    for(auto& s : str)
    {
        if(ret.empty() || ret.back() != s)
        {
            ret.push_back(s);
        }
        else 
        {
            ret.pop_back();
        }
    }

    cout << (ret.size() == 0 ? "0" : ret) << endl;

    return 0;
}

2. Day02

2.1 牛牛的快递(模拟)

  1. 题目链接BC64 牛牛的快递
  2. 题目描述:

  1. 解法:
    • 算法思路:模拟分情况讨论即可。
    • 扩展两个库函数: ceil函数和floor函数(天花板和地板)。
    • ceil函数功能:向上取整。返回不小于输入值的最小整数(即向正无穷方向取整)。
    • floor函数功能:向下取整。返回不大于输入值的最大整数(即向负无穷方向取整)。
  2. C++ 算法代码:
#include <iostream>
#include <cmath>
using namespace std;

int main() 
{
    double a = 0;
    char b;
    cin >> a >> b;

    int ret = 0;
    if(a <= 1)
    {
        ret += 20;
    }
    else 
    {
        ret += 20;
        a -= 1;
        ret += ceil(a);
    }

    if(b == 'y')
    {
        ret += 5;
    }

    cout << ret << endl;

    return 0;
}

2.2 最小花费爬楼梯(动态规划 - 线性 dp)

  1. 题目链接DP4 最小花费爬楼梯
  2. 题目描述:

  1. 解法:
    • 算法思路:简单线性 dp。
  2. C++ 算法代码:
#include <iostream>
using namespace std;

const int N = 1e5 + 10;

int main() 
{
    int n = 0;
    cin >> n;

    int cost[N];
    for(int i = 0; i < n; i++)
    {
        cin >> cost[i];
    }

    int dp[N];
    dp[0] = cost[0];
    dp[1] = cost[1];
    for(int i = 2; i < n; i++)
    {
        dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i];
    }

    cout << min(dp[n - 1], dp[n - 2]) << endl;
    
    return 0;
}

2.3 数组中两个字符串的最小距离(模拟 + 贪心)

  1. 题目链接[编程题]数组中两个字符串的最小距离
  2. 题目描述:

  1. 解法:
    • 算法思路:小贪心,或者是小 dp。
    • 用 prev1 标记 i 位置之前最近一次出现的第一个字符串的下标;
    • 用 prev2 标记 i 位置之前最近一次出现的第二个字符串的下标;
  2. C++ 算法代码:
#include <iostream>
using namespace std;

int main() 
{
    int n = 0;
    cin >> n;
    string s1, s2;
    string s;
    cin >> s1 >> s2;

    int prev1 = -1;
    int prev2 = -1;
    int ret = 0x3f3f3f3f;
    for(int i = 0; i < n; i++)
    {
        cin >> s;
        if(s == s1)
        {
            if(prev2 != -1)
            {
                ret = min(ret, i - prev2);
            }

            prev1 = i;
        }
        else if(s == s2)
        {
            if(prev1 != -1)
            {
                ret = min(ret, i - prev1);
            }

            prev2 = i;
        }
    }

    if(ret == 0x3f3f3f3f)
    {
        cout << -1 << endl;
    }
    else
    {
        cout << ret << endl;
    }

    return 0;
}

3. Day03

3.1 简写单词(模拟)

  1. 题目链接BC149 简写单词
  2. 题目描述:

  1. 解法:
    • 算法思路:简单模拟题,主要是处理⼀下输入的问题。
  2. C++ 算法代码:
#include <cctype>
#include <iostream>
#include <string>
using namespace std;

char Mytoupper(char c)
{
    if(c >= 'a' && c <= 'z')
    {
        return c -= 32;
    }

    return c;
}

int main()
{
    string str;
    while(cin >> str)
    {
        char c = Mytoupper(str[0]);
        cout << c;
    }

    return 0;
}

3.2 dd爱框框(滑动窗口)

  1. 题目链接dd爱框框
  2. 题目描述:

  1. 解法:
    • 算法思路:基础同向双指针算法。
  2. C++ 算法代码:
#include <iostream>
using namespace std;;

const int N = 1e7 + 10;

int main()
{
    int n = 0;
    int x = 0;
    cin >> n >> x;
    
    int arr[N] = {0};
    for(int i = 0; i < n; i++)
    {
        // cin >> arr[i];
        scanf("%d", &arr[i]);
    }
    
    int left = 0;
    int right = 0;
    int retleft = 0;
    int retright = N;
    int sum = 0;
    while(right < n)
    {
        sum += arr[right];
        while(sum >= x)
        {
            if(right - left < retright - retleft)
            {
                retright = right;
                retleft = left;
            }
            
            sum -= arr[left++];
        }
        
        right++;
    }
    
    cout << retleft + 1 << " " << retright + 1 << endl; // retleft和retright是下标,加1才是区间
    
    return 0;
}

3.3 除2!(贪心 + 堆)

  1. 题目链接除2!
  2. 题目描述:

  1. 解法:
    • 算法思路:搞⼀个堆模拟⼀下就好了。堆顶的元素就是偶数的最大值,这里用到了贪心。
  2. C++ 算法代码:
#include <iostream>
#include <queue>
using namespace std;;

int main()
{
    int n = 0;
    int k = 0;
    cin >> n >> k;
    
    priority_queue<long long> heap;
    long long x = 0;
    long long sum = 0;
    
    while(n--)
    {
        cin >> x;
        sum += x;
        if(x % 2 == 0)
        {
            heap.push(x);
        }
    }
    
    while(heap.size() && k--)
    {
        long long tmp = heap.top() / 2;
        heap.pop();
        sum -= tmp;
        if(tmp % 2 == 0)
        {
            heap.push(tmp);
        }
    }
    
    cout << sum << endl;
    
    return 0;
}

4. Day04

4.1 Fibonacci数列(Fib 数列)

  1. 题目链接WY22 Fibonacci数列
  2. 题目描述:

  1. 解法:
    • 算法思路:求斐波那契数列的过程中,判断⼀下:何时 n 会在两个 fib 数之间。
  2. C++ 算法代码:
#include <iostream>
using namespace std;

int main() 
{
    int n = 0;
    cin >> n;

    int a = 0;
    int b = 1;
    int c = 1;

    while(n > c)
    {
        a = b;
        b = c;
        c = a + b;
    }

    cout << min(c - n, n - b) << endl;

    return 0;
}

4.2 单词搜索(搜索)

  1. 题目链接NC242 单词搜索
  2. 题目描述:

  1. 解法:
    • 算法思路:简单的深度优先遍历的应用题~
  2. C++ 算法代码:
class Solution {
public:
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    bool vis[101][101] = { 0 };
    int m = 0;
    int n = 0;

    bool exist(vector<string>& board, string word) 
    {
        m = board.size();
        n = board[0].size();
        for(int i = 0; i < m; i++)
        {
            for(int j = 0; j < n; j++)
            {
                if(board[i][j] == word[0])
                {
                    if(dfs(board, i, j, word, 0))
                    {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    bool dfs(vector<string>& board, int i, int j, string& word, int pos)
    {
        if(pos == word.size() - 1)
        {
            return true;
        }

        vis[i][j] = true;
        for(int k = 0; k < 4; k++)
        {
            int x = i + dx[k];
            int y = j + dy[k];
            if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && board[x][y] == word[pos + 1])
            {
                if(dfs(board, x, y, word, pos + 1))
                {
                    return true;
                }
            }
        }

        vis[i][j] = false;
        return false;
    }
};

4.3 杨辉三角(动态规划)

  1. 题目链接BC140 杨辉三角
  2. 题目描述:

  1. 解法:
    • 算法思路:最基础的 dp 模型,按照规律模拟出来杨辉三⻆即可。
  2. C++ 算法代码:
#include <iostream>
#include <vector>
using namespace std;

int main() 
{
    int n = 0;
    cin >> n;
    vector<vector<int>> dp(n, vector<int>(n));

    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j <= i; j++)
        {
            if(j == 0 || i == j)
            {
                dp[i][j] = 1;
            }
            else 
            {
                dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
            }

            printf("%5d", dp[i][j]);
        }

        cout << endl;
    }

    return 0;
}

5. Day05

5.1 游游的you(贪心 + 模拟)

  1. 题目链接[编程题]游游的you
  2. 题目描述:

  1. 解法:
    • 算法思路。由题意得:you 和 oo 是相互独立的;但是 you 的分值更高,因此我们应该优先去拼凑 you,然后再考虑 oo。
  2. C++ 算法代码:
#include <iostream>
using namespace std;

int main() 
{
    int q = 0;
    cin >> q;
    int a, b, c;
    while(q--)
    {
        cin >> a >> b >> c;
        int num1 = min(min(a, b), c);
        int num2 = b - num1 - 1 > 0 ? b - num1 - 1 : 0;
        cout << num1 * 2 + num2 << endl;
    }

    return 0;
}

5.2 腐烂的苹果(多源 BFS)

  1. 题目链接NC398 腐烂的苹果
  2. 题目描述:

  1. 解法:
    • 算法思路:多源 BFS 问题,固定套路~
  2. C++ 算法代码:
class Solution {
public:
    int m, n;
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};

    int rotApple(vector<vector<int> >& grid) 
    {
        m = grid.size();
        n = grid[0].size();

        queue<pair<int, int>> q;
        for(int i = 0; i < m; i++)
        {
            for(int j = 0; j < n; j++)
            {
                if(grid[i][j] == 2)
                {
                    q.push({i, j});
                }
            }
        }

        int ret = 0;
        while(q.size())
        {
            int sz = q.size();
            ret++;
            while(sz--)
            {
                auto [a, b] = q.front();
                q.pop();
                for(int i = 0; i < 4; i++)
                {
                    int x = a + dx[i];
                    int y = b + dy[i];
                    if(x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1)
                    {
                        q.push({x, y});
                        grid[x][y] = 2;
                    }
                }
            }
        }

        for(int i = 0; i < m; i++)
        {
            for(int j = 0; j < n; j++)
            {
                if(grid[i][j] == 1)
                {
                    return -1;
                }
            }
        }

        return ret - 1;  // 当最后一次队列不为空的时候其实不需要在蔓延了,因为已经全部都是2,所以ret会多加一次
    }
};

5.3 孩子们的游戏(约瑟夫环)

  1. 题目链接JZ62 孩子们的游戏(圆圈中最后剩下的数)
  2. 题目描述:

  1. 解法:
    • 算法思路:
      • 解法一:模拟。这里不多赘述,用数组或者链表模拟均可。但是数据量大的话会超时…
      • 解法二:数学规律。通过画图,可以找到相邻两次删除坐标的规律。通过递推关系,求出剩下的最后⼀个数是哪个。
  2. C++ 算法代码:
class Solution {
public:
    int LastRemaining_Solution(int n, int m) 
    {
        int f = 0;
        for(int i = 2; i <= n; i++)
        {
            f = (f + m) % i;
        }
        
        return f;
    }
};

6. Day06

6.1 大数加法(高精度加法)

  1. 题目链接NC1 大数加法
  2. 题目描述:

  1. 解法:
    • 算法思路:模版类型的算法题,模拟加法列竖式运算的过程即可。
  2. C++ 算法代码:
class Solution {
public:
    string solve(string s, string t) 
    {
        string ret;
        int m = s.size() - 1;
        int n = t.size() - 1;
        int c = 0;
        while(m >= 0 || n >= 0 || c)
        {
            int sm = m >= 0 ? s[m] - '0' : 0;
            int sn = n >= 0 ? t[n] - '0' : 0;
            int sum = sm + sn + c;
            c = 0;
        
            if(sum >= 10)
            {
                c = 1;
                sum %= 10;
            }

            ret.push_back(sum + '0');
            m--;
            n--;
        }

        reverse(ret.begin(), ret.end()); // 别忘逆序

        return ret;
    }
};

6.2 链表相加(二)(链表 + 高精度加法)

  1. 题目链接NC40 链表相加(二)
  2. 题目描述:

  1. 解法:
    • 算法思路:模拟高精度加法的过程,只不过是在链表中进行而已。
  2. C++ 算法代码:
/**
 * struct ListNode {
 *  int val;
 *  struct ListNode *next;
 *  ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
  public:
    // 逆序链表
    ListNode* reverse(ListNode* head) 
    {
        ListNode* newHead = new ListNode(0);
        ListNode* cur = head;
        while(cur) 
        {
            ListNode* next = cur->next;
            cur->next = newHead->next;
            newHead->next = cur;
            cur = next;
        }

        cur = newHead->next;
        delete newHead;
        return cur;
    }

    ListNode* addInList(ListNode* head1, ListNode* head2) 
    {
        // 1. 逆序
        head1 = reverse(head1);
        head2 = reverse(head2);

        ListNode* head = new ListNode(0);
        ListNode* cur = head;
        int c = 0;
        while(head1 || head2 || c)
        {
            int tmp1 = head1 == nullptr ? 0 : head1->val;
            int tmp2 = head2 == nullptr ? 0 : head2->val;
            int sum = tmp1 + tmp2 + c;
            c = 0;
            
            if(sum >= 10)
            {
                c = 1;
                sum %= 10;
            }

            ListNode* tmp = new ListNode(sum);
            cur->next = tmp;
            cur = tmp;

            if(head1)
            {
                head1 = head1->next;
            }

            if(head2)
            {
                head2 = head2->next;
            }
        }

        cur = head->next;
        delete head;
        return reverse(cur);
    }
};

6.3 大数乘法(高精度乘法)

  1. 题目链接NC10 大数乘法
  2. 题目描述:

  1. 解法:
    • 算法思路:根据列竖式运算的过程模拟即可。但是我们可以改进⼀下列竖式的过程:
      • 先计算无进位相乘并且相加后的结果;
      • 然后再处理进位。
    • 也可以正常模拟计算。
  2. C++ 算法代码:
// 第一种解法
class Solution {
public:
    string add_solve(string s, string t) 
    {
        string ret;
        int m = s.size() - 1;
        int n = t.size() - 1;
        int c = 0;
        while(m >= 0 || n >= 0 || c)
        {
            int sm = m >= 0 ? s[m] - '0' : 0;
            int sn = n >= 0 ? t[n] - '0' : 0;
            int sum = sm + sn + c;
            c = 0;
        
            if(sum >= 10)
            {
                c = 1;
                sum %= 10;
            }

            ret.push_back(sum + '0');
            m--;
            n--;
        }

        reverse(ret.begin(), ret.end()); // 别忘逆序

        return ret;
    }

    string solve(string s, string t) 
    {
        int m = s.size() - 1;
        int n = t.size() - 1;
        int c = 0;

        int count = 0;
        vector<string> ret;
        while(m >= 0)
        {
            int sm = m >= 0 ? s[m] - '0' : 0;
            int tmp = n;
            string s;  // 存放每次相乘的结果
            for(int i = 0; i < count; i++)
            {
                s += '0';
            }

            while(tmp >= 0 || c)
            {
                int sn = tmp >= 0 ? t[tmp] - '0' : 0;
                int sum = sm * sn + c;
                c = 0;

                if(sum >= 10)
                {
                    c = sum / 10;
                    sum %= 10;
                }

                s.push_back(sum + '0');

                tmp--;
            }

            ret.push_back(s);
            count++;
            m--;
        }

        reverse(ret[0].begin(), ret[0].end());
        for(int i = 1; i < ret.size(); i++)
        {
            reverse(ret[i].begin(), ret[i].end());
            ret[i] = add_solve(ret[i - 1], ret[i]);
        }

        if(ret[ret.size() - 1][0] == '0')
        {
            return "0";
        }

        return ret[ret.size() - 1];
    }
};

// 第二种解法
class Solution
{
public:
    string solve(string s, string t)
    {
        reverse(s.begin(), s.end());
        reverse(t.begin(), t.end());
        int m = s.size(), n = t.size();
        vector<int> tmp(m + n);
        
        // 1. ⽆进位相乘相加
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                tmp[i + j] += (s[i] - '0') * (t[j] - '0');
            }
        }
        
        // 2. 处理进位
        int c = 0;
        string ret;
        for (auto x : tmp)
        {
            c += x;
            ret += c % 10 + '0';
            c /= 10;
        }
        while (c)
        {
            ret += c % 10 + '0';
            c /= 10;
        }
        
        // 3. 处理前导零
        while (ret.size() > 1 && ret.back() == '0') ret.pop_back();
        reverse(ret.begin(), ret.end());
        return ret;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Smile丶凉轩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值