1. 程式人生 > >POJ-1511 Invitation Cards (單源最短路+逆向)

POJ-1511 Invitation Cards (單源最短路+逆向)

每一個 一個 nvi names ring 我們 最短路算法 span class

<題目鏈接>

題目大意:

有向圖,求從起點1到每個點的最短路然後再回到起點1的最短路之和。

解題分析:

在求每個點到1點的最短路徑時,如果僅僅只是遍歷每個點,對它們每一個都進行一次最短路算法,那麽即使是用了堆優化的dijkstra,時間復雜度也高達O(n^2 logn),而本題有1000000個點,毫無疑問,這種想法必然是不可行的,所以我們可以采用逆向思維,將圖中的每一條有向邊全部反向,然後以1為起點,僅做一次dijkstra,就能得到1到所有點的最短距離,即反向前的,所有點到1點的最短距離。所以,本題的正解應為:先以1為起點,做一次dijkstra,算出,1到所有點的最短距離,然後將邊反向,再以1為起點,做一次dijkstra,此時就能得到,其他所有點到1的最短距離,將所有的最短距離相加,即為答案。時間復雜度為O(nlogn)。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <iostream>
using namespace std;

#define INF 0x3f3f3f3f
const int maxn =1000000+100;

int n,m;
struct Edge{
    int to;
    int next;
    int w;
};

Edge edge[maxn],redge[maxn];

struct NODE{
    
int index; int dis; bool operator < (NODE const &tmp)const{ return dis>tmp.dis; } }d[maxn]; int dist[maxn]; int cnt,rcnt,head1[maxn],head2[maxn],vis[maxn]; void init(){ memset(head1,-1,sizeof(head1)); memset(head2,-1,sizeof(head2)); cnt=0,rcnt=0; } void add1(int
u,int v,int w){ edge[cnt].to=v;edge[cnt].w=w; edge[cnt].next=head1[u]; head1[u]=cnt++; } void add2(int u,int v,int w){ redge[rcnt].to=v;redge[rcnt].w=w; redge[rcnt].next=head2[u]; head2[u]=rcnt++; } void dijkstra1(int st){ for(int i=1;i<=n;i++){ vis[i]=0;d[i].dis=INF,d[i].index=i; } priority_queue<NODE>q; d[st].dis=0;q.push(d[st]); while(!q.empty()){ int u=q.top().index; q.pop(); if(vis[u])continue; vis[u]=1; for(int i=head1[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(d[v].dis>d[u].dis+edge[i].w){ d[v].dis=d[u].dis+edge[i].w; q.push(d[v]); } } } } void dijkstra2(int st){ //因為正、反向邊的edge[],和head[]散組不同,所以要將另外再寫一個dijkstra函數 for(int i=1;i<=n;i++){ vis[i]=0;d[i].dis=INF,d[i].index=i; } priority_queue<NODE>q; d[st].dis=0;q.push(d[st]); while(!q.empty()){ int u=q.top().index; q.pop(); if(vis[u])continue; vis[u]=1; for(int i=head2[u];i!=-1;i=redge[i].next){ int v=redge[i].to; if(d[v].dis>d[u].dis+redge[i].w){ d[v].dis=d[u].dis+redge[i].w; q.push(d[v]); } } } } int main(){ int t;scanf("%d",&t); while(t--){ scanf("%d %d",&n,&m); init(); for(int i=1;i<=m;i++){ int a,b,c; scanf("%d %d %d",&a,&b,&c); add1(a,b,c); //存儲該有向圖正確的邊 add2(b,a,c); //將該有向圖的所有邊反向存儲 } long long sum=0; dijkstra1(1); //邊未反向之前,求出1到所有點的最短路 for(int i=2;i<=n;i++){ sum+=d[i].dis; } dijkstra2(1); //將邊反向後,求出所有點到1點的最短路 for(int i=2;i<=n;i++){ sum+=d[i].dis; } printf("%lld\n",sum); } return 0; }

2018-08-27

POJ-1511 Invitation Cards (單源最短路+逆向)