POJ1511 Invitation Cards
阿新 • • 發佈:2019-01-10
題意:
n個點,m條有向帶權邊,權值為正表示花費。設從節點1到其他所有點的總花費為A,從其他所有點到節點1的總花費為B。不同點之間通行的花費將重複計數(即:假設x到y的路徑與x到z的路徑經過同一條邊,這條邊的花費會被計數兩次,具體看樣例體會)。問A+B的最小值是多少?
分析:
從節點1到其他所有點即為求1到其他所有點的最短路徑之和。無負權邊則可用dijkstra演算法,資料量比較大可考慮用優先佇列優化。從其他各點回來的時候,我們將圖各邊反向,這等價於求反向後的圖中1到其他各點的最短路徑之和。這裡我用前向星法存邊,具體的反向操作看程式碼。
時限8000ms,以下程式碼25052K,2204ms過。
程式碼:
#include<cstdio> #include<cstring> #include<queue> using namespace std; typedef __int64 ll; #define N 1000005 #define INF (1<<30) struct Edge{ int to,w,next; }; Edge edge[N];bool vis[N];ll ans; int head[N],cnt,dist[N],n,m,head2[N]; priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q; void addedge(int u,int v,int w){ edge[cnt].to=v;edge[cnt].w=w;edge[cnt].next=head[u];head[u]=cnt++; } void dijkstra(){ int u,v; while(!q.empty()) q.pop(); for(int i=1;i<=n;++i){dist[i]=INF;vis[i]=false;} dist[1]=0;q.push(make_pair(0,1)); while(!q.empty()){ u=q.top().second;q.pop(); if(vis[u]) continue; vis[u]=true;ans+=(ll)dist[u]; for(int i=head[u];i!=-1;i=edge[i].next){ v=edge[i].to; if(!vis[v]&&dist[v]>dist[u]+edge[i].w){ dist[v]=dist[u]+edge[i].w; q.push(make_pair(dist[v],v)); } } } } void rev(){ int u,v; for(int i=1;i<=n;++i){head2[i]=head[i];head[i]=-1;} for(int i=1;i<=n;++i){ for(int j=head2[i];j!=-1;){ u=j;v=edge[j].to; j=edge[j].next;edge[u].to=i; edge[u].next=head[v];head[v]=u; } } } int main(){ int t,u,v,w;scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m);cnt=0; for(int i=1;i<=n;++i) head[i]=-1; for(int i=0;i<m;++i){ scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); }ans=0; dijkstra();rev();dijkstra(); printf("%I64d\n",ans); } return 0; }