当前位置: 首页 > news >正文

【烧脑算法】枚举:有序穷举,分步排查

前言

本文是继上一篇【入门算法】枚举:有序穷举,分步排查-CSDN博客,本文将对枚举技巧进行更深一步的分析,将会解析一些难题中如何使用枚举技巧,在一些面试题中枚举的可能不再是变量,也有可能是一段区间。总而言之就是将多变量转化为单变量。

PS:本篇博客中的所有题目均来自于灵茶山艾府 - 力扣(LeetCode)分享的题单。 

枚举进阶题目剖析

1031. 两个非重叠子数组的最大和

题解:枚举右,维护左。此题的特殊之处在于枚举的不再是单个数据,而是一段数组的总和。

class Solution {
public:int maxSumTwoNoOverlap(vector<int>& nums, int firstLen, int secondLen) {int n=nums.size();auto all_max=[&](int first,int second){int left=0,right=0;for(int i=0;i<first;i++) left+=nums[i];   //求开始时长度为firstLen的和for(int i=first;i-first<second;i++) right+=nums[i];   //求开始时长度为secondLen的和int ret=left+right,tmp=left;        //tmp来存储左边区间依次向右移动后的区间和结果for(int i=first+second;i<n;i++)     {right+=nums[i]-nums[i-second];   //枚举右tmp+=nums[i-second]-nums[i-second-first];left=max(left,tmp);ret=max(ret,left+right);}return ret;};//两个区间没有前后之分,所以多需要求一遍return max(all_max(firstLen,secondLen),all_max(secondLen,firstLen));  }
};

2555. 两个线段获得的最多奖品

题解:枚举有,维护左。与上题一样此题枚举和维护的都是一段区间,并且两端区间都是滑动窗口。具体方法见下面题解。

class Solution {
public:int maximizeWin(vector<int>& nums, int k) {int n=nums.size();//控制两个区间,保持区间内的最大差控制在k以内int i=0;for(i=1;i<n;i++) if(nums[i]-nums[0]>k) break;   //先找到前面的第一个区间//此时[0,i)是一个区间int l1=0,r1=i-1,l2=i;       //[l1,r1][l2,i]分别表示两个区间int tmp=r1-l1+1,ret=0;for(i=l2+1;i<n;i++)         //枚举右边的区间,维护左边区间的最大值{while(nums[i]-nums[l2]>k){l2++,r1++;while(nums[r1]-nums[l1]>k) l1++;tmp=max(tmp,r1-l1+1);}ret=max(ret,tmp+i-l2+1);}if(l2==n-1) ret=max(ret,tmp+1);  //处理当一个区间就能包含n-1个个数,即不会进入for循环的情况else if(l2==n) ret=max(ret,tmp);       //处理一个区间就能包含所有数的情况return ret;}
};

1995. 统计特殊四元组

题解:本题数据量小能直接使用暴力求解,四个循环O(N^4)。那能否进行优化将时间复杂度降低一些???

可以使用枚举右,维护做。对等式进行变形:nums[d]-nums[c]=nums[a]+nums[b],枚举右边所有的nums[d]-nums[c],统计左边所有的nums[a]+nums[b]即可。根据题意a<b<c<d所以在统计nums[a]+num[b]的时候应该以c看作分界线,主循环枚举c,依次枚举c左侧的数据作为d看左边有没有满足条件的数据,当c向后走时,再对左边数据和进行扩充即可。

class Solution {
public:int countQuadruplets(vector<int>& nums) {//对等式进行变形: nums[d]-nums[c]=nums[a]+nums[b]//枚举右侧的nus[d]-nums[c],将左边所有的nums[a]+nums[b]进行统计int n=nums.size();unordered_map<int,int> m; //存储左边nums[a]+nums[b]的和m[nums[0]+nums[1]]++;int  ret=0;for(int c=2;c<n-1;c++){for(int d=c+1;d<n;d++)  //枚举c右边的数据作为d{int need=nums[d]-nums[c];if(m.count(need)) ret+=m[need];}for(int j=0;j<c;j++)  //将c位置的数据添加到左边的哈希表中m[nums[j]+nums[c]]++;}return ret;}
};

3404. 统计特殊子序列的数目

题解:与前两题类似,枚举右,维护左。先对等式进行变形,将最小的两个字母和最大的两个字符放到两边:对等式进行变形: nums[p]/nums[q]=nums[s]/nums[r]。此题需要特别注意的是边界情况的控制:q - p > 1 ,r - q > 1 且 s - r > 1 。具体细节见下面代码注释。

class Solution {
public:long long numberOfSubsequences(vector<int>& nums) {//对等式进行变形: nums[p]/nums[q]=nums[s]/nums[r]//枚举右侧的nums[s]/nums[r],将左边所有的 nums[p]/nums[q]进行统计int n=nums.size();unordered_map<double,int> m; //存储左边 nums[p]/nums[q]的值for(int i=0;i<3;i++)   //注意需要间隔一个字符存储{for(int j=i+2;j<3;j++)m[(double)nums[i]/nums[j]]++;}long long ret=0;for(int r=4;r<n-2;r++){for(int s=r+2;s<n;s++)  //枚举s右边的数据作为r{double need=(double)nums[s]/nums[r];if(m.count(need)) ret+=m[need];}for(int p=r-1-2;p>=0;p--)   //将以r-1为分母的商添加到哈希表中m[(double)nums[p]/nums[r-1]]++;}return ret;}
};

3267. 统计近似相等数对 II

枚举右,维护左。

枚举右:将该枚举到的数进行至多两次交换,将所有交换的数统计到set中存储,遍历set中的所有数据在左边找看有没有相等的。

细节:30----->03------>3,30通过交换可以变成3,但是3通过交换不能变成30,也就是说如果30.......3,30在3的前面就会导致3向前找时没有找30的情况,所以要先对数组进行一次排序

具体代码实现,如下。

class Solution {
public:int countPairs(vector<int>& nums) {int n=nums.size();sort(nums.begin(),nums.end());   //先进行排序,防止数小的无法交换为数大的unordered_map<int,int> m;int ret=0;for(int i=0;i<n;i++){unordered_set<int> tmp;   //存储当前数据经过之多两次交换后的值tmp.insert(nums[i]);       //存储数字本身string str=to_string(nums[i]);    //将整数转化为字符串方便交换int len=str.size();  for(int i=0;i<len;i++){for(int j=i+1;j<len;j++) {swap(str[i],str[j]);tmp.insert(stoi(str));   //交换一次for(int p=i+1;p<len;p++){for(int q=i+1;q<len;q++){swap(str[p],str[q]);   //交换两次tmp.insert(stoi(str));swap(str[p],str[q]);    //换回来}}swap(str[i],str[j]);   //换回来}}for(auto x:tmp)ret+=m.count(x)?m[x]:0;   //遍历交换后的数字,在前面找又没有相同的m[nums[i]]++;   //将当前位置加入和左边进行维护}return ret;}
};

总结

在上面题目中,有各种各样的枚举技巧,有些是3个三个变量的,4个变量的或者是区间的。只要抓住一个关键位置,来对多变量进行简化,转化为单变量的问题即可。

http://www.lqws.cn/news/470629.html

相关文章:

  • 植物神经小知识
  • 教育培训APP源码核心功能开发详解:直播、考试、组卷系统全拆解
  • 力扣1546. 和为目标值且不重叠的非空子数组的最大数目
  • 1. 常见K线组合
  • 【STM32笔记】F1F4 STM32初识、MDK调试、HAL简介
  • 3.10 坐标导航
  • C++ 函数模板
  • 【基础算法】贪心 (一) :简单贪心
  • JavaWeb后端部分
  • win2003_ddk.3790里面有windbg--6.1.0017.2----备忘
  • 【环境配置】在Ubuntu Server上安装5090 PyTorch环境
  • Python 正确重载运算符(增量赋值运算符)
  • C++重点知识详解(命名空间,缺省参数,函数重载)
  • 【舞蹈】编排:如何对齐拍子并让小节倍数随BPM递减
  • 两个python独立进程通信
  • Kubernetes 节点故障自愈方案:结合 Node Problem Detector 与自动化脚本
  • Java面试题025:一文深入了解数据库Redis(1)
  • 自定义 Hook:在 Vue3 中复用逻辑
  • Vue3 + TypeScript + xlsx 导入excel文件追踪数据流转详细记录(从原文件到目标数据)
  • Vue+spring boot前后端分离项目搭建---小白入门
  • 2025云服务器磁盘空间告急全解析:日志管理策略与智能扩容方案
  • 98. 验证二叉搜索树
  • Redis哨兵模式的学习(三)
  • React JSX语法
  • Hologres 使用 FDW
  • 「Linux文件及目录管理」输入输出重定向与管道
  • 网络编程及原理(六):三次握手、四次挥手
  • 什么是跨域问题?后端如何解决跨域问题?
  • 基于FPGA的白噪声信号发生器verilog实现,包含testbench和开发板硬件测试
  • ffmpeg(六):图片与视频互转命令