2316.统计无向图中无法相互到达点对数
题目:
给你一个整数 n ,表示一张 无向图 中有 n 个节点,编号为 0 到 n - 1 。同时给你一个二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示节点 ai 和 bi 之间有一条 无向 边。
请你返回 无法互相到达 的不同 点对数目 。
思路:
很容易看出目的考察并查集,由于要求返回无法相互到达的顶点的对数,所以再给并查集多添加一个变量size,方便返回各树中有多少元素(在根节点下标存即可)
题解:
class DisjSet{
private:
vector<int> parents;
vector<int> size;
public:
DisjSet(int n):parents(n),size(n,1){
iota(parents.begin(),parents.end(),0);
}
int Find(int x){return parents[x]==x?x:(parents[x]=Find(parents[x]));}
void merge(int i,int j){
int x=Find(i),y=Find(j);
if(x==y)return ;
parents[x]=y;
size[y]+=size[x];
}
int getSize(int x){return size[x];}
};
class Solution {
public:
long long countPairs(int n, vector<vector<int>>& edges) {
DisjSet dj(n);
for(int i=0;i<edges.size();i++){
dj.merge(edges[i][0],edges[i][1]);
}
long long sum=0;
for(int i=0;i<n;i++){
sum+=n-dj.getSize(dj.Find(i));
}
return sum/2;
}
};
心得:
1)第一次实现并查集代码,很多参考了别人的,受益匪浅- -
2)此题merge时要注意如果两个元素已经在同一集合中,那么不再对其合并。无向边的性质原因,顶点前后调换不影响其边的表示,但本人的代码段默认是前者的将后者作为父节点,因此可能在size中出现点问题;
3)复习了C++的构造函数的一些使用方法,顺便学会了使用iota()函数递增赋值;
4)Find()函数使用了路径压缩的方法,将每一个元素都直接连接到其父节点上,且精简为了一行代码,很有意思
1726.同积元组
题目:
给你一个由 不同 正整数组成的数组 nums ,请你返回满足 a * b = c * d 的元组 (a, b, c, d) 的数量。其中 a、b、c 和 d 都是 nums 中的元素,且 a != b != c != d 。
思路:
首先找四个数然后*8是显而易见的。一开始我想的是排序后双指针前后遍历一下,因为abcd各不相同所以必然是两边乘积等于中间的,但是具体实现有些阻碍遂放弃。同样,由于abcd各不相同,所以乘积的两个乘数组也不会重复,直接两层循环将所有两数乘积哈希记录,然后用数学的思想排列组合一下即可。
题解:
class Solution {
public:
int tupleSameProduct(vector<int>& nums) {
int sum=0,n=nums.size();
unordered_map<int,int> m;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
m[nums[i]*nums[j]]++;
}
}
for(auto &[k,v]:m){
sum+=v*(v-1)*4;
}
return sum;
}
};
心得:
1)主要还是思路问题,转换为数学题就容易的多了;
2)学到了map遍历值的for方法:&[key , value]。
11.盛最多水的容器
题目:
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
思路:
起初思路是对于线 i 而言,遍历0~i-1计算保存面积最大值,如果遇到 i 前第一条大于或等于 i 的长度的线,那么就跳出循环(因为此时 i 作为短边,容器最多也就只能容纳那么多水,因为是第一条大于它的所以面积的“长”此时也是最长的),最后返回存的maxx,此种方法见以下的第一条代码段。
看了题解于是学到了双指针的办法,两边取边,谁短移谁。此种见以下第二条代码段。
题解:
贴一下原始版本和双指针版本:
/*原始版本*/
class Solution {
public:
int maxArea(vector<int>& height) {
int maxx=0;
for(int i=1;i<height.size();i++){
for(int j=0;j<i;j++){
maxx=max(maxx,(i-j)*min(height[j],height[i]));
if(height[j]>=height[i])break;
}
}
return maxx;
}
};
class Solution {
public:
int maxArea(vector<int>& height) {
int maxx=0,i=0,j=height.size()-1;
while(i!=j){
int minn=min(height[i],height[j]);
maxx=max(maxx,(j-i)*minn);
if(minn==height[i])i++;
else j--;
}
return maxx;
}
};
心得:
1)学到了新的解题思路,存水问题也有变种,后续再练一下。
2)
2530.执行k次操作后的最大分数
题目:
给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。你的 起始分数 为 0 。
在一步 操作 中:
选出一个满足 0 <= i < nums.length 的下标 i ,
将你的 分数 增加 nums[i] ,并且
将 nums[i] 替换为 ceil(nums[i] / 3) 。
返回在 恰好 执行 k 次操作后,你可能获得的最大分数。
向上取整函数 ceil(val) 的结果是大于或等于 val 的最小整数。
思路:
这题没什么特别要说的,排序后改变最大值即可。
题解:
class Solution {
public:
long long maxKelements(vector<int>& nums, int k) {
priority_queue<int> q(nums.begin(),nums.end());
long long sum=0,temp;
while(k--){
temp=q.top();
sum+=temp;
q.pop();
q.push(ceil(temp/3.0));
}
return sum;
}
};
心得:
1)此题主要是优先队列priority_queue的使用。起初用的是multiset,ac但是用时有点长了,于是又学了新的stl:优先队列。priority_queue是大根堆存储,降序排列。因为只能取top值且排序实现方式异于set和multiset的红黑树,速度要更快。在只需要返回最大值的情况下是优先使用的。
1488.避免洪水泛滥
题目:
你的国家有无数个湖泊,所有湖泊一开始都是空的。当第 n 个湖泊下雨前是空的,那么它就会装满水。如果第 n 个湖泊下雨前是 满的 ,这个湖泊会发生 洪水 。你的目标是避免任意一个湖泊发生洪水。
给你一个整数数组 rains ,其中:
rains[i] > 0 表示第 i 天时,第 rains[i] 个湖泊会下雨。
rains[i] == 0 表示第 i 天没有湖泊会下雨,你可以选择 一个 湖泊并 抽干 这个湖泊的水。
请返回一个数组 ans ,满足:
ans.length == rains.length
如果 rains[i] > 0 ,那么ans[i] == -1 。
如果 rains[i] == 0 ,ans[i] 是你第 i 天选择抽干的湖泊。
如果有多种可行解,请返回它们中的 任意一个 。如果没办法阻止洪水,请返回一个 空的数组 。
请注意,如果你选择抽干一个装满水的湖泊,它会变成一个空的湖泊。但如果你选择抽干一个空的湖泊,那么将无事发生。
思路:
先说起初自己的思路,见题想到了生产者-消费者问题的进程池,遂存了一个vector记录数组中0的下标,后面遇到哪个重复的了就把第一个空的分给他。但是,完全疏忽了可以抽水的天数 在 连续在同一个湖泊下雨之前的情况(抽水在下雨之前- -),之后又修补了一下但运行超时。这里超时是查找最早的那一天超时,比如(0 1 2 0 0 2)显然要在2最开始出现的 i = 2 后的 i = 3 天抽水,那么问题就是如何找到这个下标,答案也很明显了,如代码所示,用lower——bound函数,二分查找到第一个2后的0的位置。
题解:
class Solution {
public:
vector<int> avoidFlood(vector<int>& rains) {
int n=rains.size();
unordered_map<int,int> m;
vector<int> v(rains.size(),1);
set<int> st;
for(int i=0;i<n;i++){
if(rains[i]==0){
st.insert(i);
}
else{
v[i]=-1;
if(m.find(rains[i])!=m.end()){
auto it=st.lower_bound(m[rains[i]]);
if(it!=st.end()){
v[*it]=rains[i];
st.erase(it);
}
else{
v.clear();
return v;
}
}
m[rains[i]]=i;
}
}
return v;
}
};
心得:
1)主要是补漏lower_bound()的使用,详细都在思路里,不赘述了。