日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

2019ICPC(银川) - Delivery Route(强连通缩点+分块最短路)

發(fā)布時間:2024/4/11 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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é),需要注意一下:

  • 強連通縮點只縮與起點st有關(guān)系的點即可,其他不相連的點答案肯定是NO PATH
  • 縮點后建新圖要記得去掉重邊,目的是為了方便記錄入度進行拓撲排序
  • 每個連通塊都有不同的起點,也就是連接上一個連通塊與當(dāng)前聯(lián)通塊的邊
  • 迪杰斯特拉的最短路僅限于在每個連通塊內(nèi)部進行,而連通塊間的傳遞用拓撲實時更新每個連通塊起點的最小值即可
  • 多說無益,還是直接看代碼吧

    代碼:

    #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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。