zoukankan      html  css  js  c++  java
  • 求权值的积最小 题解

    原题点这里

    题目描述

    给定 (n) 个点的带权有向图,求从 (1)(n) 的路径中边权之积最小的简单路径。

    • 输入格式 第一行读入两个整数 (n)(m),表示共 (n) 个点 (m) 条边。 接下来 (m) 行,每行三个正整数 (x,y,z),表示点 (x) 到点 (y) 有一条边权为 (z) 的边。

    • 输出格式 输出仅包括一行,记为所求路径的边权之积,由于答案可能很大,因此哥输出它模 998799879987 的余数即可。

    样例输入

    3 3
    1 2 3 
    2 3 3 
    1 3 10
    

    样例输出

    9
    

    分析

    乍一看,这道题就只是个最短路而已,这道题的难点主要在于如何维护取模后的值。
    最后发现模好像会炸,高精好像也会炸。。。
    所以我们就……
    引入对数!


    对数的性质 (log(mn)=log(m)+log(n))
    其证明:
    我们以 (log_2)为例子,(log_2(n)) 表示方程 (2^x=n)(x) 的解。令(2^x=n,2^y=m)

    首先 (x+y=x+y)
    所以 (log_2(2^ x*2^y)=x+y)
    (log_2(2^ x*2^y)=log_2(2^x)+log_2(2^y))
    则可得到对数的性质:(log(mn)=log(m)+log(n))
    加入最短路优化即可。。。

    AC代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    using namespace std;
    const int INF = 0x3F3F3F3F;
    const int MAXN = 1005;
    const int mod = 9987;
    double dist[MAXN];
    int cost[MAXN][MAXN], pre[MAXN][2];
    bool vis[MAXN];
    int n, m;
    
    void Di_Set(int s) {
        for (int i = 0; i <= n; ++i) dist[i] = 1e9;
        memset(vis, 0, sizeof vis);
        dist[s] = 0;
        for (int i = 1; i <= n; i++) {
            int mi = INF, k;
            for (int j = 1; j <= n; j++) {  //蓝点集 路程最小值
                if (!vis[j] && dist[j] < mi) {
                    mi = dist[j];
                    k = j;
                }
            }
            vis[k] = true;                  //加入红点集
            for (int j = 1; j <= n; j++) {  // 更新相连的
                if (cost[k][j] > 0 && dist[k] + log(cost[k][j]) < dist[j]) { 
                //在这个时候就直接用对数进行计算
                    dist[j] = dist[k] + log(cost[k][j]);
                    pre[j][0] = k;
                    pre[j][1] = cost[k][j];
                }
            }
        }
    }
    
    int main() {
        scanf("%d %d", &n, &m);
        for (int i = 1; i <= m; i++) {
            int u, v, x;
            scanf("%d %d %d", &u, &v, &x);
            cost[u][v] = x;
        }
        Di_Set(1);  //调用
        int ans = 1;
        int pos = n;
        while (pos != 1) { //从对数转正确答案
            ans *= pre[pos][1];
            ans %= mod;
            pos = pre[pos][0];
        }
        printf("%d", ans);
        return 0;
    }
    
  • 相关阅读:
    GotoAndPlay 图论
    P1965 转圈游戏  快速幂
    双栈排序 图论
    威尔逊定理 数学
    n!mod p 的求法 数学
    P3195 [HNOI2008]玩具装箱TOY DP+优化
    loj6485. LJJ 学二项式定理
    loj6539. 奇妙数论题
    loj535. 「LibreOJ Round #6」花火
    loj534. 「LibreOJ Round #6」花团
  • 原文地址:https://www.cnblogs.com/Chain-Forward-Star/p/13868104.html
Copyright © 2011-2022 走看看