題意: 在n個點m條邊的無向圖上,有k個出口 從起點出發,每到一個點(包括起點),該點連出的邊中有d條會被封鎖 求最壞狀況下到達出口的最短路html
題解: 該題爲dijkstra算法的拓展算法
因爲求最壞狀況下的最短路,對於每一個點,顯然最優的前d條邊不能走post
對於邊u->v,必然要先獲得v到出口的最壞狀況下的最短路 才能獲得u通過該邊再到出口的最壞狀況下的最短路,也就是該邊對於u的價值 因此要從出口往回考慮spa
令f[i]表示i到出口的最壞狀況下的最短路 同dijkstra算法同樣,每一個點i能夠分爲f[i]已肯定的和f[i]未肯定的 初始時天然是對於每一個出口x,f[x]=0已肯定code
對於f[v]已肯定的點v,將邊權爲w的邊u->v以f[v]+w爲關鍵字加入小根堆中htm
對於每一個點i還要記錄cnt[i]=k,表示到i後,i連出的最優的前k條邊已被封鎖blog
每次取出堆頂對應的邊u->v(若f[u]已肯定直接彈出) 則該邊爲u連出的(除已被封鎖的邊外)最優的邊 若cnt[u]<d,該邊必然會被封鎖,那麼將cnt[u]加1,彈出堆頂 若cnt[u]=d,那麼能夠肯定f[u]=f[v]+w,再用u更新連向u的邊,彈出堆頂jade
重複這一過程直到肯定f[0]的值,該值即爲答案string
不妨思考下爲什麼不從起點開始考慮it
若從起點開始考慮,令f[i]表示從起點到i的最壞狀況下的最短路 對於f[u]已肯定的點u,將邊權爲w的邊u->v以f[u]+w爲關鍵字加入小根堆中 每次取出堆頂對應的邊u->v(若f[v]已肯定直接彈出) 若cnt[u]<d,該邊必然會被封鎖,那麼將cnt[u]加1,彈出堆頂 若cnt[u]=d,能夠肯定f[v]=f[u]+w,再用v更新v連向的邊,彈出堆頂
#include<cstdio> #include<cstring> #include<queue> using namespace std; struct Edge{ int u,v,d; }; bool operator < (const Edge &a,const Edge &b){ return a.d>b.d; } priority_queue<Edge>Heap; int n,m,K,d,f[100010],cnts[100010]; int v[2000010],__next[2000010],first[100010],w[2000010],e; void AddEdge(int U,int V,int W){ v[++e]=V; w[e]=W; __next[e]=first[U]; first[U]=e; } int main(){ int x,y,z; scanf("%d%d%d%d",&n,&m,&K,&d); for(int i=1;i<=m;++i){ scanf("%d%d%d",&x,&y,&z); AddEdge(x,y,z); AddEdge(y,x,z); } memset(f,0x7f,sizeof(f)); for(int i=1;i<=K;++i){ scanf("%d",&x); f[x]=0; cnts[x]=d+1; for(int j=first[x];j;j=__next[j]){ Heap.push((Edge){v[j],x,w[j]}); } } while(!Heap.empty()){ Edge E=Heap.top(); Heap.pop(); ++cnts[E.u]; if(cnts[E.u]==d+1){ f[E.u]=E.d; for(int i=first[E.u];i;i=__next[i]){ if(cnts[v[i]]<=d){ Heap.push((Edge){v[i],E.u,f[E.u]+w[i]}); } } } } printf("%d\n",f[0]>2000000000 ? -1 : f[0]); return 0; }