LeetCode 5699. 从第一个节点出发到最后一个节点的受限路径数(最短路,dp)

题意:
现有一个加权无向连通图。给你一个正整数 n ,表示图中有 n 个节点,并按从 1 到 n 给节点编号;
另给你一个数组 edges ,其中每个 edges[i] = [ui, vi, weighti] 表示存在一条位于节点 ui 和 vi 之间的边,
这条边的权重为 weighti 。

从节点 start 出发到节点 end 的路径是一个形如 [z0, z1, z2, ..., zk] 的节点序列,
满足 z0 = start 、zk = end 且在所有符合 0 <= i <= k-1 的节点 zi 和 zi+1 之间存在一条边。

路径的距离定义为这条路径上所有边的权重总和。
用 distanceToLastNode(x) 表示节点 n 和 x 之间路径的最短距离。
受限路径 为满足 distanceToLastNode(zi) > distanceToLastNode(zi+1) 的一条路径,
其中 0 <= i <= k-1 。

返回从节点 1 出发到节点 n 的 受限路径数 。由于数字可能很大,请返回对 1e9 + 7 取余 的结果。

数据范围:
1 <= n <= 2 * 1e4
n - 1 <= edges.length <= 4 * 1e4
edges[i].length == 3
1 <= ui, vi <= n
ui != vi
1 <= weighti <= 1e5
任意两个节点之间至多存在一条边
任意两个节点之间至少存在一条路径
解法:
首先显然要计算出点n到其他点的最短路,设点n到点i的最短路为dist[i].

接下来就是计算点1到点n的受限路径数量,
设d[i]表示点i到点n的受限路径数量,
d[x]=sum(d[v]),其中x与v之间有边,且dist[x]>dist[v],
dp一下就行了,用dfs记忆化搜索.
code:
#define PI pair<int,int>
#define ll long long
const int maxm=1e5+5;
const int mod=1e9+7;
vector<PI>g[maxm];
int mark[maxm];
ll dist[maxm];
int d[maxm];
int n;
class Solution {
public:
    void dj(){
        priority_queue<PI,vector<PI>,greater<PI> >q;
        for(int i=1;i<=n;i++)dist[i]=1e18,mark[i]=0;
        dist[n]=0;
        q.push({dist[n],n});
        while(q.size()){
            int x=q.top().second;q.pop();
            if(mark[x])continue;
            mark[x]=1;
            for(auto i:g[x]){
                int v=i.first,w=i.second;
                if(dist[v]>dist[x]+w){
                    dist[v]=dist[x]+w;
                    q.push({dist[v],v});
                }
            }
        }
    }
    void dfs(int x){
        if(d[x]!=-1)return ;
        if(x==n){
            d[x]=1;
            return ;
        }
        d[x]=0;
        for(auto [v,vv]:g[x]){
            if(dist[v]<dist[x]){
                dfs(v);
                d[x]=(d[x]+d[v])%mod;
            }
        }
    }
    int countRestrictedPaths(int nn, vector<vector<int>>& e) {
        //init
        n=nn;
        for(int i=1;i<=n;i++)g[i].clear();
        //
        for(auto i:e){
            int x=i[0],y=i[1],z=i[2];
            g[x].push_back({y,z});
            g[y].push_back({x,z});
        }
        dj();
        for(int i=1;i<=n;i++)d[i]=-1;
        dfs(1);
        return d[1];
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值