牛客编程巅峰赛:牛牛送快递(迪杰斯特拉)

链接:https://ac.nowcoder.com/acm/contest/9887/C
来源:牛客网
 

题目描述

牛牛为了向牛妹表达爱意,决定亲自给牛妹送上礼物。牛牛住在城市s,而牛妹住在城市t(牛牛和牛妹并不在同一座城市)。牛客国一共有n座城市,这n城市之间有m条城际高速可以让连接的城市相互通行。在牛客国,人们喜欢用组合数C_a^b来计数,所以通过某一条城际高速所需要的过路费也是用C_a^b​来表示的。并且牛客国比起加法,更偏爱乘法,所以从城市s到达城市t所需要的总花费为路径上经历过的城际高速过路费之积,即∏i∈s→tC_{a_i}^{b_i}​。牛牛了省钱想让你帮助他规划一条花费最少的路线,并把总花费告诉牛牛。由于答案可能很大请对10^9+7取模。

示例1

输入

5,5,1,5,[[1,2,1,1],[1,5,5,3],[3,5,1,1],[2,4,1,1],[4,5,2,1]]

返回值

2

说明

1->2->4->5距离最短,为C11∗C11∗C21=1∗1∗2=2C_1^1*C_1^1*C_2^1=1*1*2=2C11​∗C11​∗C21​=1∗1∗2=2

备注:

m条城际高速的格式为 u,v,a,b,表示城市u和城市v之间有城际高速,通过该城际高速所需要的过路费为C_a^b。数据保证s和t之间有通路。

1≤n≤500,1≤m≤n∗(n−1)/2,1≤b≤a≤1000,1≤s,t≤n,1≤u,v≤n,s!=t。

思路:最短路模板题,只是走过的路径不能直接用乘积计数,因为一定会爆long long的,我们考虑乘法和对数的转换,因为乘积越大,其对数肯定也越大。

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 
     * @param n int整型 有n个城市
     * @param m int整型 有m条路径
     * @param s int整型 起始城市坐标
     * @param t int整型 终点城市坐标
     * @param edge int整型二维数组 边的格式如下:[[u1,v1,a1,b1],[u2,v2,a2,b2],...]
     * @return int整型
     */
    class node{
        int v;
        double w1;
        long w2;
        public node(int v,double w1,long w2){
            this.v=v;
            this.w1=w1;
            this.w2=w2;
        }
    }
    private boolean[] vis;
    private double[] dis1;
    private long[] dis2;
    private int mod=1000000007;
    private List<List<node>> edges;
    public int minDist (int n, int m, int s, int t, int[][] edge) {
        // write code here
        vis=new boolean[n+1];
        dis1=new double[n+1];
        dis2=new long[n+1];
        edges=new ArrayList<>();
        long[][] c2=new long[1005][1005];
        double[][] c1=new double[1005][1005];
        for(int i=0;i<=1000;i++)
            c1[i][0]=c2[i][0]=1;
        for(int i=1;i<=1000;i++)
            for(int j=1;j<=i;j++){
                c1[i][j]=c1[i-1][j]+c1[i-1][j-1];
                c2[i][j]=(c2[i-1][j]+c2[i-1][j-1])%mod;
            }
        for(int i=1;i<=1000;i++)
            for(int j=1;j<=i;j++)
                c1[i][j]=Math.log(c1[i][j]);
        for(int i=0;i<=n;i++)
            edges.add(new ArrayList<>());
        for(int[] e : edge){
            int u=e[0],v=e[1],a=e[2],b=e[3];
            edges.get(u).add(new node(v,c1[a][b],c2[a][b]));
            edges.get(v).add(new node(u,c1[a][b],c2[a][b]));
        }
        for(int i=1;i<=n;i++){
            dis2[i]=1;
            dis1[i]=1e9;
        }
        dj(s);
        return (int)dis2[t];
    }
    private void dj(int start){
        Queue<Integer> q=new LinkedList<>();
        q.add(start);
        dis1[start]=0;
        vis[start]=true;
        while(!q.isEmpty()){
            int now=q.poll();
            vis[now]=false;
            for(node nxt : edges.get(now)){
                if(dis1[nxt.v]>dis1[now]+nxt.w1){
                    dis1[nxt.v]=dis1[now]+nxt.w1;
                    dis2[nxt.v]=dis2[now]*nxt.w2%mod;
                    if(!vis[nxt.v]){
                        q.add(nxt.v);
                        vis[nxt.v]=true;
                    }
                }
            }
        }
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值