贪心算法--
1.柠檬水找零
link:860. 柠檬水找零 - 力扣(LeetCode)
code
class Solution {
public:bool lemonadeChange(vector<int>& bills) {// 贪心算法, 优先花出大面额bill, 尽可能保护小面额billint five = 0, ten = 0;// 不同bill数量for(int bill : bills){if(bill == 5) five++;else if(bill == 10){ten++;if(five <= 0) return false;else five--;}else // bill == 20{if(five >= 1 && ten >= 1) five--, ten--;// 贪心else if(five >= 3) five -= 3;else return false;}}return true;}
};
交换论证法 证明:
2.将数组和减半的最少操作次数
link:2208. 将数组和减半的最少操作次数 - 力扣(LeetCode)
code
class Solution {
public:int halveArray(vector<int>& nums) {int ans = 0;double sum = 0.0;priority_queue<double> q;for(int& e : nums){sum += e;q.push(e);}double target = sum/2.0;while(target > 0){ans++;double top = q.top(); q.pop();target -= top / 2;q.push(top / 2);}return ans;}
};
交换论证法 证明:
3.最大数
link: 179. 最大数 - 力扣(LeetCode)
key:
此问题中比较两个字符串大小:return a + b > b + a, 而不是直接return a > b;
code
class Solution {
public:string largestNumber(vector<int>& nums) {vector<string> strs;for(int num:nums){strs.push_back(to_string(num));}sort(strs.begin(), strs.end(), [](string a, string b){return a + b > b + a;// key:不要直接使用内置的字典排序(return a > b)});string ans;for(string str:strs) ans += str;if(ans[0] == '0') return "0";return ans;}
};
4.摆动序列
link:376. 摆动序列 - 力扣(LeetCode)
tips:
本题也可以使用动态规划解决, 时间复杂度O(n^2)
若使用贪心算法解决此问题,时间复杂度为O(n)
本题中如何实现贪心?
画出折线图, 选出所有极值即可。极值个数即为最长摆动序列长度
证明贪心正确性:反证法或交换论证法
code1:
class Solution {
public:int wiggleMaxLength(vector<int>& nums) {auto it = unique(nums.begin(), nums.end());nums.erase(it, nums.end());if(nums.size() <= 2) return nums.size();int flag;// flag == 0表示摆动序列第一个为大值,1为小值flag = nums[0] > nums[1] ? 0 : 1;int ans = 1;// 第一个一定参与for(int i = 1; i < nums.size(); i++){if(flag == 0)// 找极小值{if(nums[i] < nums[i-1]) continue;else {ans++;flag = !flag;}}else// 找极大值{if(nums[i] > nums[i-1]) continue;else// nums[i-1]就是极大值{ans++;flag = !flag;}}}ans++; // 最后一个一定也为最长子序列return ans;}
};
code2:
class Solution {
public:int wiggleMaxLength(vector<int>& nums) {// 求 波峰个数 + 波谷个数即可auto it = unique(nums.begin(), nums.end());nums.erase(it, nums.end());if(nums.size() <= 2) return nums.size();int ans = 2;// nums首尾都是波峰/波谷for(int i = 1; i < nums.size() - 1; i++)// 判断除首尾的中间部分是否为波峰/波谷{if((nums[i] - nums[i-1]) * (nums[i] - nums[i+1]) > 0)// nums[i]是波峰/波谷{ans++;}}return ans;}
};
5.最长递增子序列
link:300. 最长递增子序列 - 力扣(LeetCode)
贪心思路:
只关心长度为x的递增子序列的 最后元素的 最小值
code
class Solution {
public:int lengthOfLIS(vector<int>& nums) {// arr[i]:长度为i+1的严格递增子序列的最小末尾值vector<int> arr;arr.push_back(nums[0]);for(int i = 1; i < nums.size(); i++){if (nums[i] > arr.back()) {arr.push_back(nums[i]);continue;}// 找arr中第一个 >= nums[i] 的元素,替换为nums[i]int left = 0, right = arr.size()-1;for(; left < right; ){int mid = (left + right) / 2;if(arr[mid] >= nums[i]) right = mid;else left = mid + 1;}arr[left] = nums[i];}return arr.size();}
};
6.递增的三元子序列
link:334. 递增的三元子序列 - 力扣(LeetCode)
code
class Solution {
public:bool increasingTriplet(vector<int>& nums) {// 和300.最长递增子序列 类似, 不过此题更简单// arr[i]表示i+1长度的递增子序列中最小的结尾数vector<int> arr;arr.push_back(nums[0]);for(int i = 1; i < nums.size(); i++){if(nums[i] > arr.back()){arr.push_back(nums[i]);if(arr.size() >= 3) return true;continue;}else{// 在arr中二分查找第一个>=nums[i]的数, 替换为nums[i]int left = 0, right = arr.size() - 1;while(left < right){int mid = (left + right) >> 1;if(arr[mid] >= nums[i]) right = mid;else left = mid + 1;}arr[left] = nums[i];}}return false;}
};
7.最长连续递增序列
link:674. 最长连续递增序列 - 力扣(LeetCode)
这是道简单题, 没什么好说的
code
class Solution {
public:int findLengthOfLCIS(vector<int>& nums) {// 贪心 + 双指针int ans = 1;for(int i = 0; i < nums.size(); i++){int j = i + 1;while(j < nums.size() && nums[j] > nums[j-1]) j++;ans = max(ans, j - i);}return ans;}
};
8.买卖股票的最佳时机
link:121. 买卖股票的最佳时机 - 力扣(LeetCode)
由暴力枚举做优化, 用minn标记之前股票最低价位作为买入点
贪心正确性:
一定正确, 因为其由暴力枚举优化而来, 暴力枚举一定正确, 则贪心也一定正确
code
class Solution {
public:int maxProfit(vector<int>& prices) {// 暴力枚举 -> 贪心int minn = prices[0];int ans = 0;for(int i = 1; i < prices.size(); i++){int profit = prices[i] - minn > 0 ? prices[i] - minn : 0;ans = max(ans, profit);minn = min(minn, prices[i]);}return ans;}
};
9.买卖股票的最佳时机II
link:122. 买卖股票的最佳时机 II - 力扣(LeetCode)
与I不同的是, 此问题可以进行无数次交易
任意相邻两天, 只要能获得正收益, 就进行交易
code1:拆分交易
将交易都拆分为一天的交易,任意相邻两天, 只要能获得正收益, 就进行交易(一次买卖)
class Solution {
public:int maxProfit(vector<int>& prices) {// 拆分交易int ans = 0;int preVal = prices[0];for(int i = 1; i < prices.size(); i++){ans += max(prices[i] - preVal, 0);preVal = prices[i];}return ans;}
};
code2 :双指针
低点买入, 高点卖出
class Solution {
public:int maxProfit(vector<int>& prices) {// 双指针,低点买入,高点卖出// 双指针适合用来寻找连续的,具有某种性质的区间int n = prices.size();int ans = 0;for(int i = 0; i < n; i++){int j = i; while(j + 1 < n && prices[j] < prices[j + 1]) j++;ans += prices[j] - prices[i];i = j;// 不用手动给i置为最低点}return ans;}
};
9.k次取反后最大化的数组和
link:1005. K 次取反后最大化的数组和 - 力扣(LeetCode)
tip:
注意STL中最小堆的定义方法, 使用模板方法指明大堆/小堆:
priority_queue<int, vector<int>, greater<int>> pq(nums.begin(), nums.end());// 小堆
code
class Solution {
public:int largestSumAfterKNegations(vector<int>& nums, int k) {// 贪心:每次都取最小值进行取反priority_queue<int, vector<int>, greater<int>> pq(nums.begin(), nums.end());// 小堆while(k--){int top = pq.top();pq.pop();pq.push(-top);}int ans = 0;while(pq.size()){ans += pq.top(); pq.pop();}return ans;}
};
10.按身高排序
link:2418. 按身高排序 - 力扣(LeetCode)
tip:
本题并不是贪心算法题, 只是一道普通排序题,为下一道题做铺垫
code1:绑定排序
将heights与names绑定在一起, 按照heights排序
class Solution {
public:vector<string> sortPeople(vector<string>& names, vector<int>& heights) {vector<tuple<int, string>> hn;for(int i = 0; i < names.size(); i++){hn.push_back({heights[i], names[i]});}sort(hn.begin(), hn.end(), [](tuple<int, string>& hn1, tuple<int, string>& hn2){return get<0>(hn1) > get<0>(hn2);}// 手动实现greater);vector<string> ans;for(tuple<int, string> e:hn){ans.push_back(get<1>(e));}return ans;}
};
code2(非常实用):对下标排序
新建数组indexs从0到heights.size()-1,对应heights下标,再根据heights对indexs排序
class Solution {
public:vector<string> sortPeople(vector<string>& names, vector<int>& heights) {// 下标排序vector<int> indexs;for(int i = 0; i < names.size(); i++){indexs.push_back(i);}sort(indexs.begin(), indexs.end(), [&heights](int i, int j){return heights[i] > heights[j];});vector<string> ans;for(int i:indexs){ans.push_back(names[i]);}return ans;}
};
11.优势洗牌
link:870. 优势洗牌 - 力扣(LeetCode)
key:
用最小的 大于nums2[i]的nums1元素来匹配nums[i]
剩下的nums1元素用来匹配剩下的nums2元素,每次用最小的nums1匹配最大的nums2
可以使用交换论证法来证明贪心策略正确性
code
class Solution {
public:vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2) {// 田忌赛马// 用最小的 大于nums2[i]的nums1元素来匹配nums[i]// 剩下的nums1元素用来匹配剩下的nums2元素,每次用最小的nums1匹配最大的nums2int n = nums1.size();vector<int> indexs2;// nums2的下标映射for(int i = 0; i < nums2.size(); i++){indexs2.push_back(i);}sort(nums1.begin(), nums1.end());sort(indexs2.begin(), indexs2.end(), [&nums2](int i, int j){return nums2[i] < nums2[j];});// 用index2代替nums2排序 // 双指针vector<int> tmp(n, 0);for(int left = 0, right = n - 1, p1 = 0; left <= right; p1++){if(nums1[p1] > nums2[indexs2[left]]){tmp[left++] = nums1[p1];}else {tmp[right--] = nums1[p1];}}// 恢复排序vector<int> ans(n, 0);for(int i = 0; i < n; i++){ans[indexs2[i]] = tmp[i];}return ans;}
};
12.最长回文串
link:409. 最长回文串 - 力扣(LeetCode)
这是道简答题,不多说明
code
class Solution {
public:int longestPalindrome(string s) {int hash[256];memset(hash, 0, sizeof hash);for(char ch:s){hash[ch]++;}int arr[2] = {0};for(int e : hash){arr[0] += e/2 * 2;arr[1] += e%2;}int ans = arr[0] + (arr[1] ? 1 : 0);return ans;}
};
13.增减字符串匹配
link:942. 增减字符串匹配 - 力扣(LeetCode)
贪心策略:每次I选最小, 每次D选最大
证明贪心策略正确性:归纳法
当s[i] = ‘I'时, ans[i]选择当前最小值,则之后所有ans都比ans[i]大,这种情况一定满足题意
同理可证s[i] = ‘D',ans[i]选当前最大值, 则之后所有ans都比ans[i]小, 这种情况一定满足题意
code
class Solution {
public:vector<int> diStringMatch(string s) {// 贪心, 每次I选最小, 每次D选最大int n = s.size();int minn = 0, maxx = n;vector<int> ans(n + 1, 0);for(int i = 0; i < n; i++){ans[i] = s[i] == 'I' ? minn++ : maxx--;}ans[n] = minn;//此时minn = maxxreturn ans;}
};
14.分发饼干
link:455. 分发饼干 - 力扣(LeetCode)
其实就是田忌赛马, 相当于询问田忌赛马最多胜利的场数
贪心策略:优先先满足最小胃口孩子的需求(用最小的满足其胃口的饼干)
贪心策略正确性证明见本文第11题“优势洗牌”(交换论证法)
code
class Solution {
public:int findContentChildren(vector<int>& g, vector<int>& s) {// 贪心策略:优先先满足最小胃口孩子的需求(用最小的满足其胃口的饼干),类似田忌赛马int ans = 0;sort(g.begin(), g.end());sort(s.begin(), s.end()); for(int ps = 0, pg = 0; ps < s.size() && pg < g.size(); ps++){printf("s[ps] = %d, g[pg] = %d\n", s[ps], g[pg]);if(s[ps] >= g[pg]){ans++;pg++;}}return ans;}
};
15.最优除法
link:553. 最优除法 - 力扣(LeetCode)
贪心策略:让nums[0]为分子,其他均为分母即可
key:nums[0]比为分子,nums[1]必为分母
由于nums[i] >2,易得贪心策略一定为最优解
code
class Solution {
public:string optimalDivision(vector<int>& nums) {// 贪心策略:让nums[0]为分子,其他均为分母即可// key:nums[0]比为分子,nums[1]必为分母if(nums.size() == 1) return to_string(nums[0]);if(nums.size() == 2) return to_string(nums[0]) + '/' + to_string(nums[1]);string ans;ans += to_string(nums[0]) + "/(" + to_string(nums[1]);for(int i = 2; i < nums.size(); i++){ans += '/' + to_string(nums[i]);}ans += ')';return ans;}
};
16.跳跃游戏II
link:45. 跳跃游戏 II - 力扣(LeetCode)
贪心策略:选择能跳的最远的点作为下一个点(选择i + nums[i]最大的i点)
code
class Solution {
public:int jump(vector<int>& nums) {// 贪心策略:选择能跳的最远的点作为下一个点(选择i + nums[i]最大的i点)int ans = 0;int cur = 0;int n = nums.size();while(cur < nums.size()-1){// 题目保证可以到达终点则nums[cur] != 0if(nums[cur] == 0){printf("error, nums[cur] == 0\n");return -1;}int nextStep = cur + 1, farest = cur + nums[cur];if(farest >= n - 1) return ans + 1;for(int i = cur + 1; i <= cur + nums[cur]; i++){if(i + nums.at(i) > farest){nextStep = i;farest = i + nums.at(i);}}cur = nextStep;ans++;}return ans;}
};
17.跳跃游戏
link:55. 跳跃游戏 - 力扣(LeetCode)
与跳跃游戏II类似,不多解释
code
class Solution {
public:bool canJump(vector<int>& nums) {// 贪心策略:每次选择能跳的最远的点为下一个点(选令i+nums[i]最大的i最为下一个点// 下一个点的nums[i]为0则return falseint left = 0, right = 0;// 下一个点的选取区间int nextStep = 0, farest = 0 + nums[0];int cur = 0;while(cur < nums.size() - 1){if(nums[cur] == 0) return false;left = cur + 1;right = cur + nums[cur];if(right >= nums.size()-1) return true;nextStep = cur + 1;for(int i = left; i <= right; i++){if(i + nums[i] > farest){farest = i + nums[i];nextStep = i;}}cur = nextStep;}return true;}
};
18.加油站
link:134. 加油站 - 力扣(LeetCode)
本题贪心方案不是很明显,值得仔细分析
贪心策略:
每次只从diff[i] >= 0的位置开始,若遇到不能递达的站点j,则更新i为j([i, j]内所有站点都不满足条件)
diff[i] = gas[i] - cost[i]
code
class Solution {
public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {// 暴力枚举 -> 贪心(改变i更新逻辑)vector<int> diff;for(int i = 0; i < gas.size(); i++){diff.push_back(gas[i]-cost[i]);}for(int i = 0; i < diff.size(); i++){if(diff[i] < 0) continue;int sum = 0;for(int cur = i; cur < i + diff.size(); cur++){sum += diff[cur % diff.size()];if(sum < 0){i = cur;// key:改变i的更新逻辑break;}}if(sum >= 0) return i;}return -1;}
};
19.单调递增的数字
link:738. 单调递增的数字 - 力扣(LeetCode)
贪心策略:若n合法,直接返回n; 若n不合法,返回最大位-1,后接多个9(此策略有暴力枚举优化而来)
贪心原理正确性:如果n本身不满足条件,为了保持递增性, 最后一个递增元素一定要减1, 在此情况下将其后所有元素置为9,既满足递增条件,又保证是最大值。即为最优解。
也可以使用反证法证明贪心策略正确性,自行证明比答案大的数不满足递增条件即可
code
class Solution {
public:int monotoneIncreasingDigits(int n) {// 贪心策略:若n合法,直接返回n; 若n不合法,返回最大位-1,后接多个9(此策略有暴力枚举优化而来)// 如果n本身不满足条件,为了保持递增性, 最后一个递增元素一定要减1, 在此情况下将其后所有元素置为9,即为最优解if(n <= 9) return n;string sn = to_string(n);bool valied = true;int end = 0;// 最后一个递增元素的下标for(int i = 0; i < sn.size() - 1; i++){if(sn[i] > sn[i + 1]) {valied = false;end = i;break;}}if(valied) return n;string ans = sn.substr(0, end) + func(sn.substr(end, string::npos));return monotoneIncreasingDigits(stoi(ans));// 防止s[i-1] > s[i]}string func(string str){string ret = to_string(str[0] - '0' - 1);for(int i = 1; i < str.size(); i++){ret += "9";}return ret;}
};
20.坏了的计算器
link:991. 坏了的计算器 - 力扣(LeetCode)
key:正难则反,交换startValue与target ,/ - 改为 * +
贪心策略:能除就除
code
class Solution {
public:int brokenCalc(int startValue, int target) {// 正难则反,转化为target->startValue, 只能/2 或 +1swap(startValue, target);int ans = 0;while(startValue != target){if(startValue % 2 == 1) {startValue += 1;}else{if(startValue > target) startValue /= 2;else startValue += 1;}ans++;}return ans;}
};
21.合并区间
link:56. 合并区间 - 力扣(LeetCode)
贪心策略:每次选择最小start最小的区间
code
class Solution {
public:vector<vector<int>> merge(vector<vector<int>>& intervals) {// 贪心策略:每次选择最小start最小的区间sort(intervals.begin(), intervals.end(), [](vector<int>& vc1, vector<int>& vc2){return vc1[0] < vc2[0];});vector<vector<int>> ans;int left = intervals[0][0], right = intervals[0][1];for(int i = 1; i < intervals.size(); i++){int start = intervals[i][0], end = intervals[i][1];if(start <= right){right = max(right, end);}else{ans.push_back({left, right});left = start, right = end;}}ans.push_back({left, right});return ans;}
};
22.无重叠区间
link:435. 无重叠区间 - 力扣(LeetCode)
正难则反:求使得区间互不重叠的区间的最大数量
贪心策略:优先选择终点最小的区间
tips:
一般来讲,区间问题中,既可以左端点排序,也可右端点排序。本题也是如此,只不过本题中使用右端点排序更加直观,容易理解
若本题使用左端点排序,则在每次重叠时选择end最小的区间即可
区间问题常用贪心算法
code(右端点排序)
class Solution {
public:int eraseOverlapIntervals(vector<vector<int>>& intervals) {// 正难则反:求使得区间互不重叠的区间的最大数量// 贪心策略:优先选择终点最小的区间sort(intervals.begin(), intervals.end(), [](vector<int>& vc1, vector<int>& vc2){return vc1[1] < vc2[1];});int left = intervals[0][0], right = intervals[0][1];int maxx = 1;for(int i = 1; i < intervals.size(); i++){if(intervals[i][0] >= right) {maxx++;right = intervals[i][1];}else continue;}return intervals.size() - maxx;}
};
code2-左端点排序
每次重叠时选择end最小的区间
本解法直接求需要删去的区间的个数
class Solution {
public:int eraseOverlapIntervals(vector<vector<int>>& intervals) {// 左端点排序sort(intervals.begin(), intervals.end(), [](vector<int>& vc1, vector<int>& vc2){return vc1[0] < vc2[0];});// 删除区间int right = intervals[0][1];int ans = 0;for(int i = 1; i < intervals.size(); i++){if(intervals[i][0] < right)// 重叠{ans++;right = min(right, intervals[i][1]);}else right = intervals[i][1];}return ans;}
};
23.用最少数量的箭引爆气球
link:452. 用最少数量的箭引爆气球 - 力扣(LeetCode)
与上题类似,与重叠区间有关
贪心策略:左端点排序,顺序遍历
与前组有重叠则更新交集,不重叠就射箭全部戳破,并更新交集
code
class Solution {
public:int findMinArrowShots(vector<vector<int>>& points) {// 贪心策略:左端点排序,顺序遍历// 与前组有重叠则更新交集,不重叠就射箭全部戳破,并更新交集sort(points.begin(), points.end(), [](vector<int>& vc1, vector<int>& vc2){return vc1[0] < vc2[0];});int right = points[0][1];int ans = 0;for(int i = 1; i < points.size(); i++){if(points[i][0] > right)// 不重叠{ans++;right = points[i][1];}else right = min(right, points[i][1]);}return ans + 1;}
};
24.整数替换
link:397. 整数替换 - 力扣(LeetCode)
code1--dfs模拟
class Solution {
public:unordered_map<int, int> hash;int integerReplacement(int n) {// 直接模拟(dfs 记忆化搜索)return dfs(n);}int dfs(long long n){if(hash.count(n)) return hash[n];int ret;if(n == 1){hash[1] = 1;ret = 0;}else if(n % 2 == 0){ret = (1 + dfs(n / 2));}else{ret = 1 + min(dfs(n + 1), dfs(n - 1));}hash[n] = ret;return ret;}
};
code2--贪心
以二进制视角看待n
class Solution {
public:int integerReplacement(int n) {int ans = 0;long long cur = n;while(cur != 1){if(cur % 2 == 0) cur /= 2;else{if(cur == 3) cur-=1;else if(cur % 4 == 1) cur -= 1;else cur += 1;}ans++;}return ans;}
};
25.俄罗斯套娃信封问题
link:354. 俄罗斯套娃信封问题 - 力扣(LeetCode)
左端点排序+贪心+二分
排序后求右端点的最长递增子序列即可
tips:
排序时,若左端点相同, 则按照右端点降序排序,这样可以保证最长递增子序列不出现同左端点的元素
本题也可以用动态规划代替贪心+二分部分,虽然这样会超市(时间复杂度O(N), 但动态规划是一种应用更加广泛,也更简单易懂的算法。但是为了降低时间复杂度,本题解使用贪心算法解决本问题
code
class Solution {
public:int maxEnvelopes(vector<vector<int>>& evs) {// 转化为最长递增子序列即可(左端点排序 + 重写排序)sort(evs.begin(), evs.end(), [](vector<int>& vc1, vector<int>& vc2){if(vc1[0] != vc2[0]) return vc1[0] < vc2[0];else return vc1[1] > vc2[1];}); // 重写排序,令左端点相同的元素们按照右端点降序,保证最长递增子序列中,同左端点的元素只出现一次// 寻找右端点最长递增子序列(贪心方法)vector<int> vc(1, -1);// vc[i]表示长度为i的最长递增子序列的最小结尾值for(vector<int> ev :evs){int val = ev[1];if(val > vc.back()){vc.push_back(val);continue;}// vc 是升序的, 二分查找第一个大于等于val的元素下标int left = 1, right = vc.size() - 1;for(; left < right; ){int mid = (left + right) >> 1;if(vc[mid] >= val) right = mid;else left = mid + 1;}vc[left] = val;}// chechfor(int e:vc){printf("%d ", e);}return vc.size() - 1;}
};
26.可被三整除的最大和
link:1262. 可被三整除的最大和 - 力扣(LeetCode)
tip:
因为本题mod3,3比较小,所以可以使用贪心+分情况讨论。
但是但是如果将3换为更大的数,贪心时的分类讨论会特别麻烦,此时我们就应该使用多状态动态规划
本题code可以稍微优化:可以先将两个mod3==1与两个mod3==2的最小的数求出,避免不同情况重复求共同值
code
class Solution {
public:int maxSumDivThree(vector<int>& nums) {// 正难则反:先求nums的sum,再分类讨论如何舍去较小元素使能sum被三整除// 因为本题是mod3,3比较小,所以可以使用分类讨论+贪心,但是如果将3换为更大的数,贪心时的分类讨论会特别麻烦,此时我们就应该使用多状态动态规划sort(nums.begin(), nums.end());const int INF = 0x3f3f3f3f;int ans;int sum = 0;for(int num:nums) sum += num;if(sum % 3 == 0) return sum;else if(sum % 3 == 1) {// 情况一:去除最小的mod3 == 1的元素int a = INF;for(int num:nums){if(num % 3 == 1){a = num;break;}}// 情况二:去除最小的两个mod3 == 2的元素vector<int> b;for(int num:nums){if(num % 3 == 2){b.push_back(num);if(b.size() >= 2) break;}}int sub = a; if(b.size() >= 2) sub = min(sub, b[0] + b[1]);ans = sum - sub;} else // sum % 3 == 2{// 情况一:减去两个mod3 == 1的最小元素vector<int> a;for(int num:nums){if(num % 3 == 1){a.push_back(num);if(a.size() >= 2) break;}}// 情况二:减去最小的mod3 == 2的元素int b = INF;for(int num:nums){if(num % 3 == 2) {b = num;break;}}int sub = b; if(a.size() >= 2) sub = min(sub, a[0] + a[1]);ans = sum - sub;}return ans;}
};
27.距离相等的条形码
link:1054. 距离相等的条形码 - 力扣(LeetCode)
贪心+模拟
code1
key:只要保证所有元素都不和其前一个元素重复即可
每次选择剩余的相同数量最大的且不与前一个元素重复的num
class Solution {
public:static bool cmp(pair<int, int>& pr1, pair<int, int>& pr2){return pr1.first < pr2.first;}vector<int> rearrangeBarcodes(vector<int>& bs) {// 贪心+模拟// key:只要保证所有元素都不和其前一个元素重复即可unordered_map<int, int> num_cnt;vector<int> ans;for(int b:bs){num_cnt[b]++;}priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> pq(cmp);for(auto [num, cnt]:num_cnt){pq.push({cnt, num});}// 开始模拟pair<int, int> prePair = pq.top(); pq.pop();ans.push_back(prePair.second);prePair.first--;while(pq.size()){pair<int, int> pr = pq.top(); pq.pop();ans.push_back(pr.second);pr.first--;if(prePair.first > 0)pq.push(prePair);prePair = pr;}return ans;}
};
code2
先摆放在偶数下标位置, 后摆放再奇数下标位置。
只要保证cnt最多的num先摆放即可,剩下的数的摆放顺序无所谓(但是相同的数要连续顺序摆放)
class Solution {
public:vector<int> rearrangeBarcodes(vector<int>& bs) {// 贪心+模拟 (code2分割法unordered_map<int, int> num_cnt;int maxCnt = 0;int mostNum = 0;for(int b:bs) {if(++num_cnt[b] > maxCnt){maxCnt = num_cnt[b];mostNum = b;}}printf("maxCnt = %d, mostNum = %d\n", maxCnt, mostNum);// 模拟vector<int> ans(bs.size(), 0);for(int i = 0; i < maxCnt; i++){ans[i*2] = mostNum;}num_cnt.erase(mostNum);int idx = maxCnt * 2;for(auto& [num, cnt]:num_cnt){for(int i = 0; i < cnt; i++){if(idx >= ans.size()) idx = 1;ans[idx] = num;idx += 2;}}return ans;}
};
28.重构字符串
link:767. 重构字符串 - 力扣(LeetCode)
与上题“距离相等的条形码"相同,只不过参数从vector<int>改为了string
判断特殊情况, 之后直接复用上题代码即可
code
class Solution {
public:string reorganizeString(string s) {// 同1054.距离相等的条形码int sz = s.size();unordered_map<char, int> ch_cnt;int maxCnt = 0;char mostChar = 0;for(char ch:s) {if(++ch_cnt[ch] > maxCnt){maxCnt = ch_cnt[ch];mostChar = ch;}}if(maxCnt > (sz + 1) / 2) return "";vector<int> vc(s.begin(), s.end());vector<int> ret = rearrangeBarcodes(vc);string ans;for(char ch:ret){ans += ch;}return ans;}static bool cmp(pair<int, int>& pr1, pair<int, int>& pr2){return pr1.first < pr2.first;}vector<int> rearrangeBarcodes(vector<int>& bs) {// 贪心+模拟// key:只要保证所有元素都不和其前一个元素重复即可unordered_map<int, int> num_cnt;vector<int> ans;for(int b:bs){num_cnt[b]++;}priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> pq(cmp);for(auto [num, cnt]:num_cnt){pq.push({cnt, num});}// 开始模拟pair<int, int> prePair = pq.top(); pq.pop();ans.push_back(prePair.second);prePair.first--;while(pq.size()){pair<int, int> pr = pq.top(); pq.pop();ans.push_back(pr.second);pr.first--;if(prePair.first > 0)pq.push(prePair);prePair = pr;}return ans;}
};
相关文章:
贪心算法--
1.柠檬水找零 link:860. 柠檬水找零 - 力扣(LeetCode) code class Solution { public:bool lemonadeChange(vector<int>& bills) {// 贪心算法, 优先花出大面额bill, 尽可能保护小面额billint five 0, ten 0;// 不…...
【从0到1搞懂大模型】神经网络的实现:数据策略、模型调优与评估体系(3)
一、数据集的划分 (1)按一定比例划分为训练集和测试集 我们通常取8-2、7-3、6-4、5-5比例切分,直接将数据随机划分为训练集和测试集,然后使用训练集来生成模型,再用测试集来测试模型的正确率和误差,以验证…...
CTF工具集合-持续更新
工具地址https://github.com/huan-cdm/ctf_tools工具介绍: 1.ARCHPR:压缩包密码破解工具 2.StegSolve-1.4.jar:隐写图片查看工具 3.ctf_decrypt_tool.rar:随波逐流CTF编码工具 4.010_Editor_All_Versions_For_Windows_CracKed.…...
小方摄像头接入本地服务器的方法
最早众筹时买了几个小方摄像头,后来嫌弃分辨率,就淘汰吃灰好几年,最近想折腾个摄像头识别的小项目,秉着不投入先凑合跑起来的原则,想到了尘封已久的小方,想看看能不能通过网络拉取数据流。 搜索了下&#x…...
取反符号~
取反符号 ~ 用于对整数进行按位取反操作。它会将二进制表示中的每一位取反,即 0 变 1,1 变 0。 示例 a 5 # 二进制表示为 0000 0101 b ~a # 按位取反,结果为 1111 1010(补码表示) print(b) # 输出 -6解释 5 的二…...
Jenkins实现自动化构建与部署:上手攻略
一、持续集成与Jenkins核心价值 1.1 为什么需要自动化构建? 在现代化软件开发中,团队每日面临以下挑战: 高频代码提交:平均每个开发者每天提交5-10次代码。多环境部署:开发、测试、预发布、生产环境需频繁同步。复杂…...
爱普生温补晶振 TG5032CFN高精度稳定时钟的典范
在科技日新月异的当下,众多领域对时钟信号的稳定性与精准度提出了极为严苛的要求。爱普生温补晶振TG5032CFN是一款高稳定性温度补偿晶体振荡器(TCXO)。该器件通过内置温度补偿电路,有效抑制环境温度变化对频率稳定性的影响&#x…...
【Java 面试 八股文】计算机网络篇
操作系统篇 1. 什么是HTTP? HTTP 和 HTTPS 的区别?2. 为什么说HTTPS比HTTP安全? HTTPS是如何保证安全的?3. 如何理解UDP 和 TCP? 区别? 应用场景?3.1 TCP 和 UDP 的特点3.2 适用场景 4. 如何理解TCP/IP协议?5. DNS协议 是什么?说说DNS 完整的查询…...
OpenHarmony5.0分布式系统源码实现分析—软总线
一、引言 OpenHarmony 作为一款面向万物互联的操作系统,其分布式软总线(Distributed SoftBus)是实现设备间高效通信和协同的核心技术之一。分布式软总线通过构建一个虚拟的总线网络,使得不同设备能够无缝连接、通信和协同工作。本…...
Spring Boot/Spring Cloud 整合 ELK(Elasticsearch、Logstash、Kibana)详细避坑指南
我们在开发中经常会写日志,所以需要有个日志可视化界面管理,使用ELK可以实现高效集中化的日志管理与分析,提升性能稳定性,满足安全合规要求,支持开发运维工作。 下述是我在搭建ELK时遇到的许许多多的坑,希望…...
云原生周刊:Istio 1.25.0 正式发布
开源项目推荐 Dstack Dstack 是一个开源的 AI 计算管理平台,旨在简化 AI 任务的部署和管理。它支持本地和云端运行 AI 工作负载,并提供自动化的 GPU 资源调度,使开发者能够更高效地利用计算资源。Dstack 兼容 K8s,可以无缝集成到…...
微前端如何拯救大型项目
前言 在前端开发的世界中,我们经常会遇到这样的问题:一个大型项目往往由多个团队共同开发,每个团队负责一部分功能。然而,随着项目的不断扩大和复杂化,前端代码库变得越来越庞大和难以维护。这时,微前端&a…...
RabbitMQ 高级特性:从 TTL 到消息分发的全面解析 (下)
RabbitMQ高级特性 RabbitMQ 高级特性解析:RabbitMQ 消息可靠性保障 (上)-CSDN博客 RabbitMQ 高级特性:从 TTL 到消息分发的全面解析 (下)-CSDN博客 引言 RabbitMQ 作为一款强大的消息队列中间件ÿ…...
OpenManus-通过源码方式本地运行OpenManus,含踩坑及处理方案
前言:最近 Manus 火得一塌糊涂啊,OpenManus 也一夜之间爆火,那么作为程序员应该来尝尝鲜 1、前期准备 FastGithub:如果有科学上网且能正常访问 github 则不需要下载此软件,此软件是提供国内直接访问 githubGit&#…...
Ubuntu22.04修改root用户并安装cuda
由于本人工作原因,经常会遇到需要给ubuntu打显卡驱动的问题,虽然说不难吧,但是耐不住机器多,重复多次也就烦了,于是抽出了一点时间,并且在deepseek的帮助之下,写了一个自动安装驱动的脚本&#…...
Java LeetCode 热题 100 回顾38
干货分享,感谢您的阅读!LeetCode 热题 100 回顾_力code热题100-CSDN博客 一、哈希部分 1.两数之和 (简单) 题目描述 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两…...
MySQL复习笔记
文章目录 1.MySQL1.1什么是数据库1.2 数据库分类1.3 MySQL简介1.4连接数据库 2. 操作数据库2.1 操作数据库2.2 数据库的列类型2.3 数据库的字段属性(重点)2.4 创建数据库表(重点)2.5 数据表的类型2.6 修改数据表 3. MySQL 数据管理…...
解释 TypeScript 中的类型系统,如何定义和使用类型?
1. 类型系统的核心作用 TypeScript类型系统本质上是JavaScript的静态类型增强方案,提供三个核心价值: 开发阶段类型检查(类似编译时eslint)更清晰的API文档(类型即文档)更好的IDE自动补全支持 代码示例&…...
安裝do時出現log file support is not available
“log file support is not available (press RETURN)” 这个提示信息表明日志文件支持不可用,让你按回车键继续。出现这种情况可能是因为 Odoo 的日志相关配置存在问题或者一些必要的依赖没有正确安装配置。以下是一些可以尝试的解决办法: 1. 检查 Odo…...
[HTTP协议]应用层协议HTTP从入门到深刻理解并落地部署自己的云服务(1)知识基础
[HTTP协议]应用层协议HTTP从入门到深刻理解并落地部署自己的云服务(1)知识基础 水墨不写bug 文章目录 (一)概念梳理1.什么是协议?2.什么是应用层?3. 为什么要进行分层? (二)HTTP协议2.1 初识HTTP协议2.2HTTP协议的URL2.2.1域名2.2.2端口号2…...
机票改签请求
示例代码: tool def update_ticket_to_new_flight(ticket_no: str, new_flight_id: int) -> str:"""Update the users ticket to a new valid flight.Args:ticket_no (str): The ticket number to be updated.new_flight_id (int): The ID of th…...
linux下文件读写操作
Linux下,文件I/O是操作系统与文件系统之间进行数据传输的关键部分。文件I/O操作允许程序读取和写入文件,管理文件的打开、关闭、创建和删除等操作。 1. 文件描述符 在Linux中,每个打开的文件都由一个文件描述符来表示。文件描述符是一个非负…...
命名管道的创建和通信实现
目录 命名管道的创建 使用函数创建命名管道的通信 预备创建 makefile设计 server.hpp设计 clent.hpp设计 comm.hpp设计 server.cc设计 clent.cc设计 测试运行 今天我们来学习命名管道 由于匿名管道(pipe())无法在两个毫不相干的进程之间进行通…...
C++和OpenGL实现3D游戏编程【连载24】——父物体和子物体之间的坐标转换
欢迎来到zhooyu的C++和OpenGL游戏专栏,专栏连载的所有精彩内容目录详见下边链接: 🔥C++和OpenGL实现3D游戏编程【总览】 父子物体的坐标转换 1、本节要实现的内容 前面章节我们了解了父物体与子物体的结构,它不仅能够表示物体之间的层次关系,更重要的一个作用就是展示物…...
21.HarmonyOS Next CustomSlider组件步长控制教程(三)
温馨提示:本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦! 文章目录 1. 步长控制概述2. 步长基本概念2.1 什么是步长?2.2 步长的作用 3. 设置步长3.1 基本参数3.2 代码示例 4. 步长与范围的关系4…...
小白学习:rag向量数据库
学习视频: https://www.bilibili.com/video/BV11zf6YyEnT/?spm_id_from333.337.search-card.all.click 例子: 用户提出问题 客服机器人基于rag回答用户问题 过程拆解: 客户问题 – 转化为向量表示 – 在向量数据库中进行相似性搜索 – 系…...
STM32 CAN模块原理与应用详解
目录 概述 一、CAN模块核心原理 1. CAN协议基础 2. STM32 CAN控制器结构 3. 波特率配置 二、CAN模块配置步骤(基于HAL库) 1. 初始化CAN外设 2. 配置过滤器 3. 启动CAN通信 三、数据收发实现 1. 发送数据帧 2. 接收数据帧(中断方式…...
NO.29十六届蓝桥杯备战|string九道练习|reverse|翻转|回文(C++)
P5015 [NOIP 2018 普及组] 标题统计 - 洛谷 #include <bits/stdc.h> using namespace std;int main() {ios::sync_with_stdio(false);cin.tie(nullptr);string s;getline(cin, s);int sz s.size();int cnt 0;for (int i 0; i < sz; i){if (isspace(s[i]))continue…...
最新版本TOMCAT+IntelliJ IDEA+MAVEN项目创建(JAVAWEB)
前期所需: 1.apache-tomcat-10.1.18-windows-x64(tomcat 10.1.8版本或者差不多新的版本都可以) 2.IntelliJ idea 24年版本 或更高版本 3.已经配置好MAVEN了(一定先配置MAVEN再搞TOMCAT会事半功倍很多) 如果有没配置…...
MAC-禁止百度网盘自动升级更新
通过终端禁用更新服务(推荐) 此方法直接移除百度网盘的自动更新组件,无需修改系统文件。 步骤: 1.关闭百度网盘后台进程 按下 Command + Space → 输入「活动监视器」→ 搜索 BaiduNetdisk 或 UpdateAgent → 结束相关进程。 2.删除自动更新配置文件 打开终端…...
Unity DOTS从入门到精通之EntityCommandBufferSystem
文章目录 前言安装 DOTS 包ECBECB可以执行的指令示例: 前言 DOTS(面向数据的技术堆栈)是一套由 Unity 提供支持的技术,用于提供高性能游戏开发解决方案,特别适合需要处理大量数据的游戏,例如大型开放世界游…...
【AIGC系列】6:HunyuanVideo视频生成模型部署和代码分析
AIGC系列博文: 【AIGC系列】1:自编码器(AutoEncoder, AE) 【AIGC系列】2:DALLE 2模型介绍(内含扩散模型介绍) 【AIGC系列】3:Stable Diffusion模型原理介绍 【AIGC系列】4࿱…...
【Linux】使用问题汇总
#1 ssh连接的时候报Key exchange failed 原因:服务端版本高,抛弃了一些不安全的交换密钥算法,且客户端版本比较旧,不支持安全性较高的密钥交换算法。 解决方案: 如果是内网应用,安全要求不这么高…...
nnUNet V2修改网络——全配置替换MultiResBlock模块
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 MultiRes Block 是 MultiResUNet 中核心组件之一,旨在解决传统 U-Net 在处理多尺度医学图像时的局…...
Git合并工具在开发中的使用指南
在团队协作开发中,Git 是最常用的版本控制工具,而代码合并(Merge)是多人协作不可避免的环节。当多个开发者同时修改同一文件的相同区域时,Git 无法自动完成合并,此时需要借助合并工具(Merge Too…...
AutoDL平台租借GPU,创建transformers环境,使用VSCode SSH登录
AutoDL平台租借GPU,创建transformers环境,使用VSCode SSH登录 一、AutoDl平台租用GPU 1.注册并登录AutoDl官网:https://www.autodl.com/home 2.选择算力市场,找到需要的GPU: 我这里选择3090显卡 3.这里我们就选择P…...
listen EACCES: permission denied 0.0.0.0:811
具体错误 npm run serve> bige-v0.0.0 serve > viteThe CJS build of Vites Node API is deprecated. See https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details. error when starting dev server: Error: listen EACCES: per…...
OpenAI API模型ChatGPT各模型功能对比,o1、o1Pro、GPT-4o、GPT-4.5调用次数限制附ChatGPT订阅教程
本文包含OpenAI API模型对比页面以及ChatGPT各模型功能对比表 - 截至2025最新整理数据:包含模型分类及描述;调用次数限制; 包含模型的类型有: Chat 模型(如 GPT-4o、GPT-4.5、GPT-4)专注于对话,…...
六十天前端强化训练之第十五天React组件基础案例:创建函数式组件展示用户信息(第15-21天:前端框架(React))
欢迎来到编程星辰海的博客讲解 我们已经学了14天了,再坚持坚持,马上我们就可以变得更优秀了,加油,我相信大家,接下来的几天,我会给大家更新前端框架(React),看完可以给一…...
北大一二三四版全套DeepSeek教学资料
DeepSeek学习资料合集:https://pan.quark.cn/s/bb6ebf0e9b4d DeepSeek实操变现指南:https://pan.quark.cn/s/76328991eaa2 你是否渴望深入探索人工智能的前沿领域?是否在寻找一份能引领你从理论到实践,全面掌握AI核心技术的学习…...
计算机网络:计算机网络的组成和功能
计算机网络的组成: 计算机网络的工作方式: 计算机网络的逻辑功能; 总结: 计算机网络的功能: 1.数据通信 2.资源共享 3.分布式处理:计算机网络的分布式处理是指将计算任务分散到网络中的多个节点(计算机或设备&…...
管理网络安全
防火墙在 Linux 系统安全中有哪些重要的作用? 防火墙作为网络安全的第一道防线,能够根据预设的规则,对进出系统的网络流量进行严格筛选。它可以阻止未经授权的外部访问,只允许符合规则的流量进入系统,从而保护系统免受…...
音频进阶学习十九——逆系统(简单进行回声消除)
文章目录 前言一、可逆系统1.定义2.解卷积3.逆系统恢复原始信号过程4.逆系统与原系统的零极点关系 二、使用逆系统去除回声获取原信号的频谱原系统和逆系统幅频响应和相频响应使用逆系统恢复原始信号整体代码如下 总结 前言 在上一篇音频进阶学习十八——幅频响应相同系统、全…...
Redis7系列:设置开机自启
前面的文章讲了Redis和Redis Stack的安装,随着服务器的重启,导致Redis 客户端无法连接。原来的是Redis没有配置开机自启。此文记录一下如何配置开机自启。 1、修改配置文件 前面的Redis和Redis Stack的安装的文章中已经讲了redis.config的配置…...
word甲烷一键下标
Sub 甲烷下标()甲烷下标 宏Selection.Find.ClearFormattingSelection.Find.Replacement.ClearFormattingWith Selection.Find.Text "CH4".Replacement.Text "CHguoshao4".Forward True.Wrap wdFindContinue.Format False.MatchCase False.MatchWhole…...
SSH 连接中主机密钥验证失败问题的解决方法
问题描述 在尝试通过 SSH 建立连接时,出现以下错误信息: WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack…...
网络安全工具nc(NetCat)
NetCat是一个非常简单的Unix工具,可以读、写TCP或UDP网络连接(network connection)。它被设计成一个可靠的后端(back-end)工具,能被其它的程序程序或脚本直接地或容易地驱动。同时,它又是一个功能丰富的 网络调试和开发工具,因为它…...
探索在生成扩散模型中基于RAG增强生成的实现与未来
概述 像 Stable Diffusion、Flux 这样的生成扩散模型,以及 Hunyuan 等视频模型,都依赖于在单一、资源密集型的训练过程中通过固定数据集获取的知识。任何在训练之后引入的概念——被称为 知识截止——除非通过 微调 或外部适应技术(如 低秩适…...
【Linux】37.网络版本计算器
文章目录 1. Log.hpp-日志记录器2. Daemon.hpp-守护进程工具3. Protocol.hpp-通信协议解析器4. ServerCal.hpp-计算器服务处理器5. Socket.hpp-Socket通信封装类6. TcpServer.hpp-TCP服务器框架7. ClientCal.cc-计算器客户端8. ServerCal.cc-计算器服务器9. 代码时序1. 服务器启…...
3.6c语言
#define _CRT_SECURE_NO_WARNINGS #include <math.h> #include <stdio.h> int main() {int sum 0,i,j;for (j 1; j < 1000; j){sum 0;for (i 1; i < j; i){if (j % i 0){sum i;} }if (sum j){printf("%d是完数\n", j);}}return 0; }#de…...