[洛谷P1730] 最小密度路径

类型:Floyd

传送门:>Here<

题意:定义一条路径密度 = 该路径长度 / 边数。给出一张$DAG$,现有$Q$次询问,每次给出$X,Y$,问$X,Y$的最小密度路径($N leq 50$)

解题思路

由于$N$非常小,考虑$Floyd$求最短路。但是这题与$Floyd$的不同就在于需要除以边数

可以枚举边的数量。在边的数量$k$确定时,只需要求得恰好经过$k$条边的最短路即可。有没有联想到矩阵乘法……但是这道题是要求先预处理之后询问,因此矩阵乘法的$log M$优化就没有意义了,因为不管怎样$M$条边的最短路都要求出来

$f[i][j][k]$表示路径$(i,j)$恰好经过$k$条边的最短路。于是我们易得$$f[i][j][k]=Min{ f[i][p][k-g]+f[p][j][g] }$$其中$p$枚举中介点,$g$枚举边数的中介点,$i,j,k$都要扫,于是复杂度$O(n^5)$……

考虑省去一层循环。我们发现刚才的推法会有很多重复的情况,事实上既然已经枚举了中介点$p$,枚举$g$就没有意义了。我们就当$g=1$,因为$g eq 1$的情况一定会归属于$p$为其他节点时的情况。因此$$f[i][j][k]=Min{ f[i][p][k-1]+f[p][j][1] }$$

Code

会有重边,所以邻接矩阵赋值时要打最小值

/*By DennyQi 2018.8.16*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int MAXN = 10010;
const int MAXM = 27010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x<<3) + (x<<1) + c - '0', c = getchar();return x * w;
}
int N,M,Q,x,y,z;
int f[55][55][1010];
int main(){
    memset(f, 0x3f, sizeof(f));
    N=r,M=r;
    for(int i = 1; i <= M; ++i){
        x=r,y=r,z=r;
        f[x][y][1]=Min(f[x][y][1],z);
    }
    for(int g = 2; g <= M; ++g)
        for(int k = 1; k <= N; ++k)
            for(int i = 1; i <= N; ++i)
                for(int j = 1; j <= N; ++j)
                    f[i][j][g] = Min(f[i][j][g], f[i][k][g-1] + f[k][j][1]);
    Q=r; 
    while(Q--){
        double cur,ans = 9999999.99; bool flg = 0;
        x=r, y=r;
        for(int k = 1; k <= M; ++k){
            if(f[x][y][k] != INF) flg = 1;
            cur = (double)f[x][y][k] / (double)k;
            ans = Min(ans, cur);
        }
        if(!flg){ printf("OMG!
"); continue; }
        printf("%.3f
", ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/qixingzhi/p/9488734.html