洛谷P3953 逛公園(spfa+記憶化搜尋)
阿新 • • 發佈:2018-12-18
P3953 逛公園
題目描述 策策同學特別喜歡逛公園。公園可以看成一張N個點M條邊構成的有向圖,且沒有 自環和重邊。其中1號點是公園的入口,N號點是公園的出口,每條邊有一個非負權值, 代表策策經過這條邊所要花的時間。 策策每天都會去逛公園,他總是從1號點進去,從N號點出來。 策策喜歡新鮮的事物,它不希望有兩天逛公園的路線完全一樣,同時策策還是一個 特別熱愛學習的好孩子,它不希望每天在逛公園這件事上花費太多的時間。如果1號點 到NN號點的最短路長為d,那麼策策只會喜歡長度不超過d + K的路線。 策策同學想知道總共有多少條滿足條件的路線,你能幫幫它嗎? 為避免輸出過大,答案對P取模。 如果有無窮多條合法的路線,請輸出-1。
輸入輸出格式 輸入格式: 第一行包含一個整數 TT, 代表資料組數。 接下來TT組資料,對於每組資料: 第一行包含四個整數 N,M,K,P,每兩個整數之間用一個空格隔開。 接下來MM行,每行三個整數a_i,b_i,c_i代表編號為a_i,b_i的點之間有一條權值為 c_i的有向邊,每兩個整數之間用一個空格隔開。
輸出格式: 輸出檔案包含 T 行,每行一個整數代表答案。
輸入輸出樣例 輸入樣例#1: 2 5 7 2 10 1 2 1 2 4 0 4 5 2 2 3 2 3 4 1 3 5 2 1 5 3 2 2 0 10 1 2 0 2 1 0 輸出樣例#1: 3 -1
分析:先求出每個點i到n的最短路dis[i],即對反向圖從n開始求最短路,然後考慮dp,設f[i][k]表示在i點使得dis[to]+val-dis[i]<=k的路徑方案數,轉移一下就行,如果碰到0環就返回-1;
程式碼
#include <cstdio> #include <queue> #include <cstring> #include <string> #define N 200005 #define inf 1e9 using namespace std; struct arr { int to,nxt,w; }a[N],b[N]; int n,m,k,p,ls1[N],ls2[N],l; int dis[N]; long long f[N][60]; bool vis[N],v[N][60]; void add(int x, int y, int w) { a[++l].to = y; a[l].w = b[l].w = w; a[l].nxt = ls1[x]; b[l].to = x; b[l].nxt = ls2[y]; ls1[x] = ls2[y] = l; } void spfa() { queue<int> q; for (int i = 1; i <= n; i++) { dis[i] = inf; vis[i] = false; } while (q.size()) q.pop(); dis[n] = 0; vis[n] = true; q.push(n); while (q.size()) { int u = q.front(); q.pop(); vis[u] = false; for (int i = ls2[u]; i; i = b[i].nxt) if (dis[b[i].to] > dis[u] + b[i].w) { dis[b[i].to] = dis[u] + b[i].w; if (!vis[b[i].to]) { vis[b[i].to] = true; q.push(b[i].to); } } } } int dfs(int x, int k) { if (v[x][k]) return -1; if (f[x][k]) return f[x][k]; v[x][k] = true; if (x == n) f[x][k] = 1; for (int i = ls1[x]; i; i =a[i].nxt) if (dis[a[i].to] + a[i].w - dis[x] <= k) { int tmp = dfs(a[i].to, k - (dis[a[i].to] + a[i].w - dis[x])); if (tmp == -1) return -1; else f[x][k] = (f[x][k] + tmp) % p; } v[x][k] = false; return f[x][k]; } int main() { int T; scanf("%d", &T); while (T--) { l = 0; memset(ls1, 0, sizeof(ls1)); memset(ls2, 0, sizeof(ls2)); memset(f, 0, sizeof(f)); scanf("%d%d%d%d", &n, &m, &k, &p); for (int i = 1; i <= m; i++) { int x, y, w; scanf("%d%d%d", &x, &y, &w); add(x, y, w); } spfa(); memset(v, false, sizeof(v)); printf("%d\n", dfs(1, k)); } }