『Luogu 1186』瑪麗卡 (枚舉 + SPFA)
阿新 • • 發佈:2018-09-27
pan oid times 一道 並且 reg 就是 帶來 保險
題目鏈接
題目描述
麥克找了個新女朋友,瑪麗卡對他非常惱火並伺機報復。
因為她和他們不住在同一個城市,因此她開始準備她的長途旅行。
在這個國家中每兩個城市之間最多只有一條路相通,並且我們知道從一個城市到另一個城市路上所需花費的時間。
麥克在車中無意中聽到有一條路正在維修,並且那兒正堵車,但沒聽清楚到底是哪一條路。無論哪一條路正在維修,從瑪麗卡所在的城市都能到達麥克所在的城市。
瑪麗卡將只從不堵車的路上通過,並且她將按最短路線行車。麥克希望知道在最糟糕的情況下瑪麗卡到達他所在的城市需要多長時間,這樣他就能保證他的女朋友離開該城市足夠遠。
編寫程序,幫助麥克找出瑪麗卡按最短路線通過不堵車道路到達他所在城市所需的最長時間(用分鐘表示)。
解題思路
挺簡單的一道題:
我們首先最暴力的方法就是枚舉每一條邊,然後跑一遍\(SPFA\),顯然這樣得T到飛起,m最大是\(\frac {n \times (n-1)}{2}\),炸到姥姥家去了,對吧qwq。
稍微想想就發現,其實我們不需要美劇所有的邊,因為有的邊根本不會給最短路帶來任何的影響,我們只需要枚舉最短路上的邊就好。
最短路的邊數最大是\(n-1\),跑這麽多遍\(SPFA\),復雜度比較玄學qwq,堆優化一下更保險。
ps:假如最短路不止有一條呢? 同理,我們先欽定一條路徑,那我們枚舉不在這條路上的邊時,對答案是沒有任何影響的,我們還是只用枚舉最短路上的邊就好了。
代碼
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> using namespace std; const int maxn=500500; int n,m,cnt=0; int head[maxn<<1],ne[maxn<<1],to[maxn<<1],v[maxn<<1],dis[maxn],fr[maxn],num[maxn]; bool nok[maxn<<1],vis[maxn]; struct nod{ int x; }; inline bool operator < (nod a,nod b){ return dis[a.x]>dis[b.x]; } inline void add(int f,int t,int w){ ne[++cnt]=head[f],head[f]=cnt,to[cnt]=t,v[cnt]=w; } priority_queue<nod>q; inline int SPFA(bool xx){ memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); while(!q.empty())q.pop(); vis[1]=1,dis[1]=0,q.push((nod){1}); while(!q.empty()){ nod now=q.top(); q.pop(); vis[now.x]=0; for(register int i=head[now.x];i;i=ne[i]){ if(nok[i])continue; if(dis[to[i]]>dis[now.x]+v[i]){ dis[to[i]]=dis[now.x]+v[i]; if(xx){ fr[to[i]]=now.x; num[to[i]]=i; } if(!vis[to[i]]){ vis[to[i]]=1; q.push((nod){to[i]}); } } } } return dis[n]; } int main(){ scanf("%d%d",&n,&m); for(register int i=1,a,b,c;i<=m;i++){ scanf("%d%d%d",&a,&b,&c); add(a,b,c),add(b,a,c); } SPFA(1); int now=n,ans=0; while(now!=1){ int tmp=num[now]; if(tmp&1)tmp++; else tmp--; nok[tmp]=nok[num[now]]=1; SPFA(0); ans=max(ans,dis[n]); nok[tmp]=nok[num[now]]=0; now=fr[now]; } cout<<ans<<endl; }
『Luogu 1186』瑪麗卡 (枚舉 + SPFA)