leetcode第392场周赛:带权图里旅途的最小代价(BFS或者并查集)

题目描述大致如下:
给你一个 n 个节点的带权无向图,节点编号为 0 到 n - 1 。

给你一个整数 n 和一个数组 edges ,其中 edges[i] = [ui, vi, wi] 表示节点 ui 和 vi 之间有一条权值为 wi 的无向边。
在图中,一趟旅途包含一系列节点和边。旅途开始和结束点都是图中的节点,且图中存在连接旅途中相邻节点的边。注意,一趟旅途可能访问同一条边或者同一个节点多次。
如果旅途开始于节点 u ,结束于节点 v ,我们定义这一趟旅途的 代价 是经过的边权按位与 AND 的结果。换句话说,如果经过的边对应的边权为 w0, w1, w2, …, wk ,那么代价为w0 & w1 & w2 & … & wk ,其中 & 表示按位与 AND 操作。
给你一个二维数组 query ,其中 query[i] = [si, ti] 。对于每一个查询,你需要找出从节点开始 si ,在节点 ti 处结束的旅途的最小代价。如果不存在这样的旅途,答案为 -1 。
返回数组 answer ,其中 answer[i] 表示对于查询 i 的 最小 旅途代价。

分析题目:
这个题目的核心点,就是一定要清楚相与的最小值的前提是越多的数相与那么得到的值一定是越小的,所以我们考虑到,如下几种情况:
1.如果我们要产查询的两个节点位于不同的区域联通块内,那么显然是无法到达的,显然结果就是-1
2.如果这两个节点位于同1个联通块内,那么对于他们两个之间的最短距离就是经历整个联通块内部的所有边最终到达彼此的距离就是相与的最小距离
针对这个思路我们想一下代码可以怎么写?
1.判断联通块那么我们大家最先想到的应该就是并查集了吧!所以第一种解法利用并查集判断,然后在合并过程把每个边相与的结果存到祖先节点的ans结果中即可.
2.第2个思路大家想一下,我们其实可以通过dfs或者bfs将每个节点所属的联通块进行编号,同时计算出每个联通块的相与的结果总和保存到相应编号的联通结果的1数组中去即可。

相对应的代码如下:
1.并查集思想代码:


int find(int x) {
    if (fa[x] != x) {
        fa[x] = find(fa[x]);
    }
    return fa[x];
};

vector<int> minimumCost(int n, vector<vector<int>> &edges, vector<vector<int>> &query) {
    int fa[n], ans[n];
    for(int i = 0;i < n;i++){fa[i] = 0;ans[i] = -1;}
    for (auto &e: edges) {
        int x = find(e[0]);//找到其祖先
        int y = find(e[1]);//找到祖先
        ans[y] &= e[2];
        if (x != y) {
            ans[y] &= ans[x];/.合并当前联通块的结果到祖先节点
            fa[x] = y;//合并这两个节点到1个联通块
        }
    }

    vector<int> res;
    for (auto &q: query) {
        int s = q[0], t = q[1];
        if(s == t)res.push_back(0);//这里需要注意一下如果起始和终止点不同直接为0
        else if(find(s) == find(t))res.push_back(ans[find(s)]);
        else res.push_back(-1);
    }
    return res;
}

BFS的思路代码如下:

vector<int> minimumCost(int n, vector<vector<int>>& edges, vector<vector<int>>& query) {
        vector<pair<int,int>> e[n];
        for(auto &edge : edges){
            int u = edge[0], v = edge[1], w = edge[2];
            e[u].push_back(make_pair(v, w));
            e[v].push_back(make_pair(u, w));
        }
        int m = 0,bel[n],val[n + 1];//bel保存的是每个节点属于哪个联通块,val保存的某个联通块的所有边权的与值
        memset(bel, 0, sizeof(bel));
        auto bfs = [&](int x){
            queue<int>q;
            q.push(x);
            bel[x] = ++m;
            val[m] = - 1;//这里-1相当于二进制有符号数全是1的一个机器数
            while(!q.empty()){
                int s = q.front();
                q.pop();
                for(auto &p : e[s]){
                    int v = p.first, w = p.second;
                    val[m] &= w;//将w与到m个联通块的值
                    if(!bel[v]){//bel可以当做标记数组来使用 判断其还未被合并到某个;联通块也就是未被访问
                        bel[v] = m;//将v合并到第m个联通块
                        q.push(v);
                    }
                }
            }
        };
        for(int i = 0;i < n;i++){
            if(!bel[i])bfs(i);
        }
        vector<int>ans;
        for(auto &qry : query){
            int x = qry[0],y = qry[1];
            if(x == y)ans.push_back(0);
            else if(bel[x] == bel[y])ans.push_back(val[bel[x]]);
            else ans.push_back(-1);
        }
        return ans;
    }
  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值