2019ICPC(银川) - Delivery Route(强连通缩点+分块最短路)
生活随笔
收集整理的這篇文章主要介紹了
2019ICPC(银川) - Delivery Route(强连通缩点+分块最短路)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
題目鏈接:點擊查看
題目大意:給出n個點和m條邊構(gòu)成的圖,每條邊都有權(quán)值,其中m1條邊是雙向的,且權(quán)值非負,有m2條邊是單向的,且權(quán)值可負,現(xiàn)在要求從給定起點st到其他每個點的最短路,若不存在路徑則輸出NO PATH
題目分析:看似很簡單的單源最短路的題目,因為存在了負邊權(quán)導(dǎo)致無法直接用迪杰斯特拉解決,又因為這個題目的數(shù)據(jù)經(jīng)過了特殊構(gòu)造,所以SPFA也會超時,只能另想辦法解決
這個題目的特點是,所有的雙向邊都是非負的,可以用迪杰斯特拉計算最短路,這也引導(dǎo)我們將整個圖分塊,分為一塊一塊的整體,這一步操作可以用tarjan強連通縮點來解決,又因為題目保證有向邊不會構(gòu)成環(huán),這樣在縮點后的新圖就是一個有向無環(huán)圖,一看到有向無環(huán)圖就要想拓撲排序,很顯然縮點后的新圖的最短路可以用拓撲排序來O(n)計算,時間復(fù)雜度大概是O(dijkstra)+O(topo),因為是加法關(guān)系,所以可以在題目規(guī)定的范圍內(nèi)解決
下面列出幾個小細節(jié),需要注意一下:
多說無益,還是直接看代碼吧
代碼:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=25000+100;const int M=150000+100;struct Egde {int to,next,w; }edge1[M],edge2[M];int head1[N],head2[N],low[N],dfn[N],c[N],Stack[N],d[N],in[N],num,cnt,cnt2,cnt1,dcc,n,top;bool ins[N],vis[N];vector<int>scc[N],start[N];//scc:強連通分塊 start:每個分塊的迪杰斯特拉的起點 vector<pair<int,int>>node;//node:新圖的邊(需要去重)queue<int>q;//儲存每個聯(lián)通塊的起點用于跑迪杰斯特拉void addedge1(int u,int v,int w) {edge1[cnt1].w=w;edge1[cnt1].to=v;edge1[cnt1].next=head1[u];head1[u]=cnt1++; }void addedge2(int u,int v,int w=0) {edge2[cnt2].w=w;edge2[cnt2].to=v;edge2[cnt2].next=head2[u];head2[u]=cnt2++; }void tarjan(int u) {dfn[u]=low[u]=++num;Stack[++top]=u;ins[u]=true;for(int i=head1[u];i!=-1;i=edge1[i].next){int v=edge1[i].to;if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}else if(ins[v])low[u]=min(low[u],dfn[v]);}if(dfn[u]==low[u]){cnt++;int v;do{v=Stack[top--];ins[v]=false;c[v]=cnt;scc[cnt].push_back(v);}while(u!=v);} }void build(int st)//縮點+連邊 {tarjan(st);//注意這里,只對st縮點即可for(int i=1;i<=n;i++)//遍歷每一條邊{for(int j=head1[i];j!=-1;j=edge1[j].next){int u=i;int v=edge1[j].to;if(c[u]!=c[v]&&c[u]&&c[v])//如果這條邊的兩個端點與st相通且不在同一個連通塊中{node.push_back(make_pair(c[u],c[v]));//記錄該邊為新圖的邊start[c[v]].push_back(v);//記錄哪些點可以作為該區(qū)塊的起點 }}}sort(node.begin(),node.end());//去掉重邊 node.erase(unique(node.begin(),node.end()),node.end());for(int i=0;i<node.size();i++)//重新建邊 {addedge2(node[i].first,node[i].second);in[node[i].second]++;//記錄入度} }struct Node {int to,w;Node(int TO,int W){to=TO;w=W;}bool operator<(const Node& a)const{return w>a.w;} };void Dijkstra(int st)//迪杰斯特拉 {priority_queue<Node>q;q.push(Node(st,d[st]));memset(vis,false,sizeof(vis));while(q.size()){Node cur=q.top();int u=cur.to;q.pop();if(vis[u])continue;vis[u]=true;for(int i=head1[u];i!=-1;i=edge1[i].next)//掃描出所有邊 {int v=edge1[i].to;int w=edge1[i].w;if(c[v]!=c[u])//不在同一個連通塊 continue;if(d[v]>d[u]+w)//更新 {d[v]=d[u]+w;q.push(Node(v,d[v]));}}} }void update(int x)//x為連通塊 {for(int i=0;i<scc[x].size();i++)//掃描出當(dāng)前聯(lián)通塊的所有點 {int u=scc[x][i];for(int j=head1[u];j!=-1;j=edge1[j].next)//掃描出所有與其他聯(lián)通塊相連的點{int v=edge1[j].to;int w=edge1[j].w;if(c[v]!=c[u])d[v]=min(d[v],d[u]+w);//拓撲更新最短路}}for(int i=head2[x];i!=-1;i=edge2[i].next)//拓撲掃描所有連通塊{int y=edge2[i].to;in[y]--;if(in[y]==0){for(int j=0;j<start[y].size();j++)//拓撲排序找到下一個連通塊的起點,加入隊列q.push(start[y][j]);}} }void init() {while(q.size())q.pop();for(int i=0;i<N;i++){scc[i].clear();start[i].clear();}node.clear();top=cnt=cnt1=cnt2=num=dcc=0;memset(head2,-1,sizeof(head2));memset(head1,-1,sizeof(head1));memset(low,0,sizeof(low));memset(dfn,0,sizeof(dfn));memset(c,0,sizeof(c));memset(ins,false,sizeof(ins));memset(in,0,sizeof(in));memset(d,inf,sizeof(d)); }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);init();int m1,m2,st;scanf("%d%d%d%d",&n,&m1,&m2,&st);while(m1--){int u,v,w;scanf("%d%d%d",&u,&v,&w);addedge1(u,v,w);addedge1(v,u,w);}while(m2--){int u,v,w;scanf("%d%d%d",&u,&v,&w);addedge1(u,v,w);}build(st);q.push(st);d[st]=0;while(q.size()){int u=q.front();q.pop();Dijkstra(u);if(q.empty()||c[q.front()]!=c[u])//如果已經(jīng)處理完當(dāng)前連通塊,嘗試尋找下一個連通塊update(c[u]);}for(int i=1;i<=n;i++){if(d[i]==inf)puts("NO PATH");elseprintf("%d\n",d[i]);}return 0; }?
總結(jié)
以上是生活随笔為你收集整理的2019ICPC(银川) - Delivery Route(强连通缩点+分块最短路)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UVALive - 3231 Fair
- 下一篇: CodeForces - 1220B M