类型: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 \neq 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!\n"); continue; } printf("%.3f\n", ans); } return 0; }