最小改正字符数
给定一个由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;}
};