算法训练营第二十七天打卡 | 回溯法、LeetCode 77 组合问题 笔试题 | 最小改正字符数、符合条件的不相邻结点数、完美数组

发布于:2024-05-17 ⋅ 阅读:(157) ⋅ 点赞:(0)

最小改正字符数

给定一个由0和1的字符串和该字符串中某个区间左边界和右边界,要求求出将该区间字符串变成01间隔(例如101、010)的字符串需要改动的最小字符数。

给定输入第一行是n,表示区间数,第二行是该字符串s,接下来n行是左边界l和右边界r。

题目中n满足  1 <= n <= 10^5

 1 <= s.length() <= 10^5

 1 <= l ,r <= 10^5

这道题开始直接自己构造一个等长度符合条件1开头的字符串,和一个0开头的字符串,逐个比较不同字符的个数,后来超时了。这题复杂度O(n * n)确实会超时。考试过程中也没想到前缀和。考完才想起来。这里记录一下正确解法。

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main() {
    int n;
    cin >> n;
    string s;
    cin >> s;
    int l, r;
    string s1 = "";
    string s2 = "";
    for (int i = 0; i < s.length(); i++) {
        if (i % 2 == 0) s1 += "1";
        else s1 += "0";
    }
    for (int i = 0; i < s.length(); i++) {
        if (i % 2 == 0) s1 += "0";
        else s2 += "1";
    }
    vector<int> d1(s.length() + 1, 0);
    vector<int> d2(s.length() + 1, 0);
    for (int i = 1; i <= s.length(); i++) {
        d1[i] = d1[i - 1] + abs(s1[i - 1] - s[i - 1]);
        d2[i] = d2[i - 1] + abs(s2[i - 1] - s[i - 1]);
    }
    while (n--) {
        cin >> l >> r;
        int res = min(d1[r + 1] - d1[l], d2[r + 1] - d1[l]);
        cout << res << endl;
    }
    return 0;
}

符合条件的不相邻结点数

有一棵树,有n个节点,构成一个图。要求求出不相邻节点相乘是偶数的方案数。

输入第一行是n,表示节点个数。后面是n- 1行,每行用u和v表示相邻节点序号数。

其中 1<= n <= 10^5

1 <= u <= 10^5

1 <= v <= 10^5

请给出符合条件的方案数

这题一开始用邻接矩阵 + map统计来做,爆内存了,确实二维矩阵这个规模下会爆。同样也是当时没想起来。考完后意识到也许可以map<int, vector>作为记录子节点的成员结构来实现。

代码如下:

#include <iostream>
#include <map>
#include <vector>

using namespace std;

int main() {
    int n;
    cin >> n;
    map<int, vector<int>> mymap_vec;
    int u, v;
    for (int i = 0; i < n - 1; i++) {
        cin >> u >> v;
        mymap_vec[u].push_back(v);
        mymap_vec[v].push_back(u);
    }
    int num = 0;
    for (int i = 0; i < n; i++) {
        if (i % 2 == 0 && mymap_vec[i].size() > 0) {
            num += (n - mymap_vec[i].size());
        }
        else {
            int sum = (n - 1) / 2 + 1;
            for (int j = 0; j < mymap_vec[i].size(); j++) {
                if (mymap_vec[i][j] % 2 == 0) sum--;
            }
            num += sum;
        }
    }
    cout << num / 2 << endl;
    return 0;
}

完美数组

这题是用的回溯。其实应该是写出来了,当时测试用例其实有点问题的。

给定一个数组,要求求出该数组所有非空子序列中满足完美数组要求的个数。其中完美数组是指该数组中奇数有奇数个,偶数有偶数个。

输入n表示数组元素个数,下面一行输入n个元素表示数组元素a[i](0,...,n-1)

n满足 1 <= n <= 10^5

a[i] 满足 1 <= a[i] <= 10^5

其实就是个回溯法组合问题,涉及到去重。而回溯法简单来说就是一种依靠递归进行的暴力枚举。每次挑出当前数组中一个值放入path路径数组,并进行递归,递归结束后返回到该处位置时又从path路径数组中去除该元素,如果是字符串则需要一开始保存,后面再赋值回去。这是递归逻辑。循环结束条件是path数组长度满足要求或者满足其他条件,将其值放入记录答案的数组即可。当记录数组访问下标的变量显示到达边界时也要进行处理。

去重逻辑首先要求排序,排完序后相同的值就在相邻位置了,由于在这样的情况下,该值有可能会产生的结果都由这些数中第一个数递归下去存储完了,因此后续需要将下标i移到最后一个该元素相同值位置处,并再借由循环中i++移到下一个不等于该值的元素处进行下一轮判断即可。

看代码直观些,代码如下:

#include <iostream>
#include <vector>
#include <bits/stdc++.h>
using namespace std;

int num = 0;
vector<int> path;
void backtracking(vector<int>& nums, int index, int size, int n1, int n2) {
    if (path.size() == size) {
        if (n1 % 2 == 1 && n2 % 2 == 0) num++;
        num %= (1000000000 + 7);
        return;
    }
    if (index == nums.size()) return;
    for (int i = index; i < nums.size(); i++) {
        path.push_back(nums[i]);
        if (nums[i] % 2 == 0) 
            backtracking(nums, i + 1, size, n1, n2 + 1);
        else backtracking(nums, i + 1, size, n1 + 1, n2);
        while (i + 1 < nums.size() && nums[i + 1] == nums[i]) i++;
        path.pop_back();
    }
}

int main() {
    int n;
    cin >> n;
    vector<int> nums;
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        nums.push_back(x);
    }
    sort(nums.begin(), nums.end());
    for (int i = 1; i < nums.size(); i++) {
        backtracking(nums, 0, i, 0, 0);
    }
    cout << num << endl;
    return 0;
}

算法训练营第二十七天 | 组合问题

一样的回溯法,稍微改动下即可。

class Solution {
private:
    vector<int> path;
    vector<vector<int>> res;
    void backtracking(int n, int index, int k) {
        if (path.size() == k) {
            res.push_back(path);
            return;
        }
        if (index == n + 1) return;
        for (int i = index; i <= n; i++) {
            path.push_back(i);
            backtracking(n, i + 1, k);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> combine(int n, int k) {
        backtracking(n, 1, k);
        return res;
    }
};


网站公告

今日签到

点亮在社区的每一天
去签到