力扣(LeetCode)882. 细分图中的可到达节点(C++)

spfa

将边细分成节点,如果一条边细分出 n n n 个结点,那么边的两个端点距离 n + 1 n+1 n+1 ,边长 n + 1 n+1 n+1。提示中给出,一共 3000 3000 3000 个初始结点,无向边的数目小于等于 10000 10000 10000 ,这是提示我们最多有 20000 20000 20000 条边。求最多可到达结点,直接求不知道走哪条边,再读读题,从一个源点出发找所有可到达的结点,转化成单源最短路问题。

这道题可以用堆优化的 d i j s t r a dijstra dijstra 或者 s p f a spfa spfa

s p f a spfa spfa 求出源点到所有初始结点的最短距离。维护答案变量 a n s ans ans ,当最大移动距离 ≥ \ge 源点到结点距离,统计可到达的初始结点。再统计可到达的边上结点,从边的两个端点 a / b a/b a/b 考虑 : 计算从源点到达 a ∣ ∣ b a||b ab 点后,剩余的移动距离 a r ∣ ∣ b r ar||br arbr a ∣ ∣ b a||b ab 剩余移动距离之和 a r + b r ar+br ar+br 就是他们可以在边上到达的结点数,如果前者大于后者,统计后者作为可到达结点数。

const int N = 3010,M = 20010;
int h[N],e[M],ne[M],w[M],idx;
int dis[N] ;//从源点到每个结点的最大距离
bool st[N] ;//当前结点是否在队列里
class Solution {
public:
    queue<int> qu;
    void add(int a,int b ,int c){
        e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
    }
    void spfa(){
        memset(dis,0x3f,sizeof(dis));
        dis[0]=0;//源点到自己距离是0
        qu.push(0);
        // st[0] = true;
        while(qu.size()){
            int t= qu.front();
            qu.pop();
            st[t] = false;
            for(int i = h[t];i!=-1;i=ne[i]){//遍历t的所有邻边
                int j = e[i];
                if(dis[j]>dis[t]+w[i]){//用t更新所有邻边的距离
                    dis[j] = dis[t] + w[i];
                    if(!st[j]) qu.push(j);//j如果不在队列,将j入队
                    st[j] = true;
                }
            }
        }

    }
    int reachableNodes(vector<vector<int>>& edges, int maxMoves, int n) {
        //edges[i][2] + 1 个结点
        //spfa求最短路(每个结点) 找最短路经过的所有结点。遍历所有边,判断最大移动和源点到两点的最短路的关系
        //判断两点剩余可移动距离相加,与两点之间边长的关系。
        memset(h,-1,sizeof h);
        for(vector<int> &edge:edges){
            int a = edge[0],b=edge[1],c=edge[2];
            add(a,b,c+1),add(b,a,c+1);
        }
        spfa();
        int ans = 0;
        for(int i = 0 ;i<n;i++)
            if(dis[i]<=maxMoves)
                ans++;
        for(auto& edge:edges){
            int a = edge[0],b= edge[1],c=edge[2];
            int ar = max(0,maxMoves-dis[a]),br = max(0,maxMoves-dis[b]);
            ans += min(ar+br,c);
        }
        idx = 0;//测试样例太多了,用了全局变量,记得idx置0
        return ans;
    }
};

时间复杂度 O ( n × m ) O(n\times m) O(n×m) n n n 是初始结点数, m m m 是边数。 统计初始结点和 s p f a spfa spfa 的平均时间复杂度 O ( n ) O(n) O(n) ,最坏时间复杂度 O ( n × m ) O(n\times m) O(n×m)

空间复杂度 O ( N + M ) O(N+M) O(N+M) ,结点的最大数量 N = 3000 N=3000 N=3000 ,边的最大数量 M = 20000 M=20000 M=20000 ,空间复杂度 O ( N + M ) O(N+M) O(N+M)

致语

理解思路很重要。
欢迎读者在评论区留言,答主看到就会回复的。

AC

AC

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清墨韵染

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值