題目大意:有一張$n$個點$m$條邊的有向圖,第$i$條邊有個權值$d_i$表示至少要走$d_i$條邊才能夠通過這條邊(可重複),問從點$1$到點$n$最少通過幾條邊(重複算屢次),無解輸出$-1$。$n,m\leqslant150,d_i\leqslant 10^9$ios
題解:按$d_i$排序,一條條加入轉移矩陣,能夠用鄰接矩陣算出走剛好$k$次能夠到達的位置。能夠發現,在一個行的轉移矩陣下,若點$1$能夠走到點$n$,最多走$n-1$步。因而能夠先暴力走$n-1$次,若還不能到達$n$,就用矩陣快速冪計算。複雜度$O(n^4m(\log_2 d+n))$。能夠令一個向量表示點$1$能夠到達的點,就變成向量乘矩陣,複雜度少一個$n$,已經能夠經過了。也能夠用$\mathrm{bitset}$優化乘法,複雜度$O(\dfrac{n^3m(\log_2d+n)}{\omega})$優化
卡點:無spa
C++ Code:blog
#include <cstdio> #include <iostream> #include <algorithm> #include <bitset> const int maxn = 160; typedef std::bitset<maxn> B8; int n, m; struct Matrix { B8 s[maxn]; Matrix() { for (int i = 0; i < n; ++i) s[i].reset(); } Matrix operator * (const Matrix &rhs) { Matrix res; for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) if (s[i][j]) res.s[i] |= rhs.s[j]; return res; } } base; B8 operator * (const B8 &lhs, const Matrix &rhs) { B8 res; res.reset(); for (int i = 0; i < n; ++i) if (lhs[i]) res |= rhs.s[i]; return res; } B8 R; struct Edge { int a, b, d; inline bool operator < (const Edge &rhs) const { return d < rhs.d; } } e[maxn]; int check(int N) { for (int i = 1; i <= N; ++i) { R = R * base; if (R[n - 1]) return i; } return N + 1; } void pw(int p) { Matrix b = base; for (; p; p >>= 1, b = b * b) if (p & 1) R = R * b; } int solve() { int p = 1, res = 0; for (; p <= m && e[p].d == 0; ++p) base.s[e[p].a - 1].set(e[p].b - 1); if (p == 1) return 0; R.set(0); for (int t, o; p <= m; ++p) { t = e[p].d - e[p - 1].d; if (t <= n) { o = check(t); if (o > t) res += t; else return res + o; } else { o = check(n); if (o > n) pw(t - n), res += t; else return res + o; } base.s[e[p].a - 1].set(e[p].b - 1); } int o = check(n); if (o > n) return 0; return res + o; } int main() { std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0); std::cin >> n >> m; for (int i = 1; i <= m; ++i) std::cin >> e[i].a >> e[i].b >> e[i].d; std::sort(e + 1, e + m + 1); int ans = solve(); if (!ans) std::cout << "Impossible\n"; else std::cout << ans << '\n'; return 0; }