1584.连接所有点的最小费用(Kruskal算法) | 1697. 检查边长度限制的路径是否存在(并查集)

给你一个points 数组,表示 2D 平面上的一些点,其中 points[i] = [xi, yi] 。

连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的 曼哈顿距离 :|xi - xj| + |yi - yj| ,其中 |val| 表示 val 的绝对值。

请你返回将所有点连接的最小总费用。只有任意两点之间 有且仅有 一条简单路径时,才认为所有点都已连接。

示例 1:

输入:points = [[0,0],[2,2],[3,10],[5,2],[7,0]]
输出:20
解释:

我们可以按照上图所示连接所有点得到最小总费用,总费用为 20 。
注意到任意两个点之间只有唯一一条路径互相到达。

思路:最小生成树——Kruskal算法

首先将所有边按权值大小升序排好,然后从权值最小的边开始遍历,如果这条边和最小生成树中的其它边不会形成环,则将它加入最小生成树;否则,这条边不是最小生成树的一部分,不能将它加进去。

判断是否成环需要用到并查集:对于添加的这条边,如果它的两个节点在同一连通分量里,那么添加这条边会产生环;反之,如果它的两个节点不在同一连通分量里,则添加这条边不会产生环

class UF
{
public:
    vector<int> parent;
    int count=0;
    UF(int n)//构造函数,初始化count和parent
    {
        parent.resize(n);
        count=n;
        for(int i=0;i<n;i++)
        {
            parent[i]=i;
        }
    }
    void uni(int p,int q)
    {
        int rootP=find(p);
        int rootQ=find(q);
        if(rootP==rootQ)
            return ;
        else
        {
            parent[rootP]=rootQ;
            count--;
        }
    }
    int find(int x)
    {
        if(x!=parent[x])
        {
            parent[x]=find(parent[x]);
        }
        return parent[x];
    }
};
struct Edge//用结构体来存储边的信息,如果用vector存,会超时
{
    int i,j,weight;//起点、终点、权值
    Edge(int i,int j,int weight):i(i),j(j),weight(weight)//构造函数,列表初始化
    {       
    }
};
class Solution {
public:
    //如果选择在类内定义cmp比较规则,需要定义为静态成员函数,静态成员函数属于类,没有this指针参数
    //因为成员函数默认拥有this指针作为函数参数,而sort()函数的第三个cmp函数指针参数中并没有this指针参数,
    //因此会出现输入的cmp参数和sort()要求的参数不匹配,报错
    //static bool cmp(Edge a,Edge b)
    //{
    //   return a.weight<b.weight;
    //}
    int minCostConnectPoints(vector<vector<int>>& points) {
        int n=points.size();
        vector<Edge> edges;
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                int xi=points[i][0];
                int yi=points[i][1];
                int xj=points[j][0];
                int yj=points[j][1];
                int weight=abs(xi-xj)+abs(yi-yj);
                Edge e(i,j,weight);
                edges.push_back(e);
            }
        }       
        sort(edges.begin(),edges.end(),[](Edge a, Edge b){return a.weight<b.weight; });
        UF uf(n);
        int sumMin=0;
        for(Edge e:edges)
        {
            int p=e.i;
            int q=e.j;
            int weight=e.weight;
            if(uf.find(p)==uf.find(q))//构成边的两个结点在同一连通分量中,不把它加入最小生成树
                continue;
            else//反之,则加入最小生成树
            {
                uf.uni(p,q);
                sumMin+=weight;
            }
        }
        return sumMin;
    }
};

 细节:

1.sort(iterator.begin(),iterator.end(),_callback)

(2条消息) 错误解决方法:error: reference to non-static member function must be called_alex_mist的博客-CSDN博客

2.lambda表达式,[](函数参数){函数体内的语句写完记得加 }

sort(edges.begin(),edges.end(),[](Edge a, Edge b){return a.weight<b.weight ; });

3.用“vector<int>().swap(v)”来释放容器v的空间

 

1697. 检查边长度限制的路径是否存在

给你一个 n 个点组成的无向图边集 edgeList ,其中 edgeList[i] = [ui, vi, disi] 表示点 ui 和点 vi 之间有一条长度为 disi 的边。请注意,两个点之间可能有 超过一条边 

给你一个查询数组queries ,其中 queries[j] = [pj, qj, limitj] ,你的任务是对于每个查询 queries[j] ,判断是否存在从 pj 到 qj 的路径,且这条路径上的每一条边都 严格小于 limitj 。

请你返回一个 布尔数组 answer ,其中 answer.length == queries.length ,当 queries[j] 的查询结果为 true 时, answer 第 j 个值为 true ,否则为 false 。

示例 1:

输入:n = 3, edgeList = [[0,1,2],[1,2,4],[2,0,8],[1,0,16]], queries = [[0,1,2],[0,2,5]]
输出:[false,true]
解释:上图为给定的输入数据。注意到 0 和 1 之间有两条重边,分别为 2 和 16 。
对于第一个查询,0 和 1 之间没有小于 2 的边,所以我们返回 false 。
对于第二个查询,有一条路径(0 -> 1 -> 2)两条边都小于 5 ,所以这个查询我们返回 true 。

示例 2:

输入:n = 5, edgeList = [[0,1,10],[1,2,5],[2,3,9],[3,4,13]], queries = [[0,4,14],[1,4,13]]
输出:[true,false]
解释:上图为给定数据。

 思路:并查集+贪心

给定一个查询时,我们可以遍历 edgeList 中的所有边,依次将长度小于limit 的边加入到并查集中,然后使用并查集查询 p 和 q 是否属于同一个集合。如果 p 和 q 属于同一个集合,则说明存在从 p 到 q 的路径,且这条路径上的每一条边的长度都严格小于limit,查询返回 true,否则查询返回 false。

如果 queries 的 limit 是非递减的,显然上一次查询的并查集里的边都是满足当前查询的 limit 要求的,我们只需要将剩余的长度小于limit 的边加入并查集中即可。基于此,我们首先将 edgeList 按边长度从小到大进行排序,然后将 queries 按 limit 从小到大进行排序,使用 k 指向上一次查询中不满足 limit 要求的长度最小的边,初始时 k=0。

我们依次遍历 queries:如果 k 指向的边的长度小于对应查询的 limit,则将该边加入并查集中,然后将 k 加 1,直到 k 指向的边不满足要求;最后根据并查集查询对应的 p 和 q 是否属于同一集合来保存查询的结果。

检查边长度限制的路径是否存在 - 检查边长度限制的路径是否存在 - 力扣(LeetCode)

class UF
{
public:
    UF(int n) {
        parent.resize(n);
        for(int i=0;i<parent.size();i++) {
            parent[i]=i;
        }
    }
    void uni(int p,int q) {
        int parent1=find(p);
        int parent2=find(q);
        if(parent1==parent2)
            return ;
        parent[parent1]=parent2;
    }
    int find(int x) {
        if(x!=parent[x])
        {
            parent[x]=find(parent[x]);
        }
        return parent[x];
    }
    bool connected(int p,int q) {
        return find(p)==find(q);
    }
private:
    vector<int> parent;
};
class Solution {
public:
    vector<bool> distanceLimitedPathsExist(int n, vector<vector<int>>& edgeList, vector<vector<int>>& queries) {
        UF uf(n);
        vector<bool> ans(queries.size(),false);
        sort(edgeList.begin(),edgeList.end(),[](vector<int>& a,vector<int>& b){return a[2]<b[2];});//按边长升序排序edgeList
        vector<int> index(queries.size());//记录queries数组的下标,保证排序后还能找到原位置
        iota(index.begin(),index.end(),0);//iota自动递增填充的函数,从0开始递增,填充index数组
        sort(index.begin(),index.end(),[&](int i,int j){return queries[i][2]<queries[j][2];});//queries数组顺序不变,按queries[i][2]的大小来排index数组
        int k=0;
        for(int i=0;i<queries.size();i++)
        {
            for(int j=k;j<edgeList.size();j++)
            {
                if(edgeList[j][2]<queries[index[i]][2])//只要满足边长小于限制就加入并查集中
                {
                    uf.uni(edgeList[j][0],edgeList[j][1]);
                }
                else//一旦出现边长大于等于限制
                {
                    k=j;
                    break;//跳出循环,且记录当前在edgelist中的遍历位置,下一个限制从此位置出发进行遍历
                }   
            }
            //记录结果,此时在同一连通分量中,就代表可在限制内到达
            ans[index[i]]=uf.connected(queries[index[i]][0],queries[index[i]][1]);
        }
        return ans;   
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值