【NOIP 2017】逛公園(最短路+記憶化搜尋)
阿新 • • 發佈:2018-12-30
肯定要先跑一次最短路
題目中的k 相當於允許我們走k距離的“冤枉路”
回想之前有些題是如何判斷哪些邊是屬於最短路上的 當dis[now]+edge[u].val==dis[vis] 這條邊就在最短路上
類似的 我們可以得出 dis[now]+edge[u].val-dis[vis]就是這一次走的“冤枉路”的長度
到這個地方搜尋的策略已經很明顯了 dfs(now,remain)表示當前當前點為now 還剩remain的冤枉路可以走
邊界條件:remain<0
然後發現這玩意兒不用標記vis陣列 因為就算有環 remain會一直減下去直到<0 還可以記憶化一下
不過無窮多的情況腫麼判?
可以這樣想 為什麼資料會給你有沒有0邊?
回憶最短路計數就會問你有沒有無窮多條滿足要求的路 這種情況只有可能是有0環存在
在這道題裡判0環異常容易 假如進入了0環 那肯定會繞了一圈後 又回到當前點 且remain不變
因此標記一下就好
另外 還有一個坑點 這是有向圖 很有可能有些點無法到達終點
因此還要反向搜出那些不能到達的
#include<bits/stdc++.h> #define N 100005 #define M 200005 #define INF 0x3f3f3f3f using namespace std; template<class T> inline void read(T &x) { x=0; static char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); } struct Edge { int to,next,val; }edge[2*M]; struct Node { int to,val; Node(int to,int val):to(to),val(val){} }; int n,m,k,p,tot,first[N],dis[N]; inline void addedge(int x,int y,int z) { tot++; edge[tot].to=y; edge[tot].next=first[x]; edge[tot].val=z; first[x]=tot; } vector<Node> res[N]; typedef pair<int,int> Pair; bool visit[N],able[N]; void Dijkstra(int s) { memset(dis,0x3f,sizeof(dis)); memset(visit,false,sizeof(visit)); priority_queue<Pair,vector<Pair>,greater<Pair> > heap; heap.push(make_pair(0,s)); dis[s]=0; while(!heap.empty()) { int now=heap.top().second; heap.pop(); if(visit[now]) continue; visit[now]=true; for(int u=first[now];u;u=edge[u].next) { int vis=edge[u].to; if(dis[now]+edge[u].val<dis[vis]) { dis[vis]=dis[now]+edge[u].val; heap.push(make_pair(dis[vis],vis)); } } } } void dfs1(int now) { able[now]=true; for(int i=0;i<res[now].size();i++) { int vis=res[now][i].to; if(!able[vis]) dfs1(vis); } } int f[N][55],again[N][55]; int dfs(int now,int remain) { if(remain<0) return 0; //越界了 if(again[now][remain]) return -INF; //走到零環了 普通的環不用擔心 因為remain會一直減下去 if(f[now][remain]!=-1) return f[now][remain]; //記憶化 int temp=0; //統計當前答案 again[now][remain]=1; if(now==n) temp++; //到達了n for(int u=first[now];u;u=edge[u].next) { int vis=edge[u].to; if(!able[vis]) continue; int key=dfs(vis,remain-(edge[u].val-(dis[vis]-dis[now])))%p; if(key==-INF) //零環 return -INF; else temp=(temp+key)%p; } again[now][remain]=0; f[now][remain]=temp%p; return f[now][remain]; } void init() { memset(edge,0,sizeof(edge)); memset(first,0,sizeof(first)); tot=0; memset(f,-1,sizeof(f)); memset(able,0,sizeof(able)); memset(again,0,sizeof(again)); } int main() { int T; read(T); while(T--) { init(); read(n),read(m),read(k),read(p); for(int i=1;i<=n;i++) res[i].clear(); for(int i=1,x,y,z;i<=m;i++) { read(x),read(y),read(z); addedge(x,y,z); res[y].push_back(Node(x,z)); } Dijkstra(1); dfs1(n); int Q=dfs(1,k); if(Q<0) cout<<"-1"<<'\n'; else cout<<Q<<'\n'; } }