百度地圖上有 n 個城市,城市編號依次爲 1 到 n。地圖中有若干個城市羣,編號依次爲 1 到 m。每個城市羣包含一個或多個城市;每個城市可能屬於多個城市羣,也可能不屬於任何城市羣。
地圖中有兩類道路。第一類道路是 城市之間的快速路,兩個城市 u,v 之間增加一條距離爲 c 的邊;第二類道路是 城市羣之間的高速路,連接兩個城市羣 a,b,通過這條高速路,城市羣 a 裏的每個城市與城市羣 b 裏的每個城市之間兩兩增加一條距離爲 c 的邊。圖中所有邊均爲無向邊。
輸入格式
第一行輸入 n(1≤n≤20000),m(0≤m≤20000),分別表示城市總數和城市羣總數。
接下來一共輸入 m 行。
第 i 行首先輸入一個 ki(1≤ki≤n),表示第 i 個城市羣中的城市數爲 ki。接下來輸入ki個數,表示第 i 個城市羣中每個城市的編號(保證一個城市羣內的城市編號不重複且合法,且ki之和不超過20000)。
下一行輸入一個整數 m1(0≤m1≤20000),表示有 m1條第一類道路,即城市之間的快速路。
接下來 m1行,每行輸入三個整數 ui,vi,ci(1≤ui,vi≤n),ci(1≤ci≤10^6),分別表示快速路連接的兩個城市編號和邊的距離。
下一行輸入一個整數 m2(0≤m2≤20000),表示有 m2條第二類道路,即城市羣之間的高速路。
接下來 m2行,每行輸入三個整數 ai,bi(1≤ai,bi≤m),li(1≤li≤10^6),分別表示快速路連接的兩個城市羣編號和邊的距離。
最後一行輸入 s,t(1≤s,t≤n),表示起點和終點城市編號。
輸出格式
輸出一個整數,表示城市 s 到城市 t 到最短路。如果不存在路徑,則輸出-1。
樣例說明
1 -> 2 - > 5或者1 -> 4 -> 5是最短的路徑,總長度爲 12。
樣例輸入
5 4
2 5 1
2 2 4
1 3
2 3 4
2
1 2 9
1 5 18
2
1 2 6
1 3 10
1 5
樣例輸出
12
提示
如果採用暴力建邊,邊的數量太多內存受不了,因此我們需要增加點的數量減少邊的數量。
第一類路很簡單我們就直接跳過。這題需要把每個城市羣拆成兩個點,表示該城市羣的入口和出口,因爲對於第二類路城市羣之間是互通的,不然無法避免出現在跑最短路過程中直接通過匯點跑向屬於同一個城市羣的錯誤走法。之後每個城市羣中的城市建立從城市到出口,從入口到城市的有向邊,權值爲0。對於城市羣我們建立從城市羣ai的出口到城市羣bi的入口,ai的入口到bi的出口,權值就爲題目所給的li。之後直接上最短路即可,存圖建議用前向星比較合適。
我的代碼中規定點編號爲0~m-1是m個城市羣的入口,m~2*m-1是m個城市羣的出口,2*m~2*m+n-1是n個城市。
注意跑最短路過程中可能會爆int。
示例程序
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
struct jj
{
int v,next,cost;
}w[1000000];
int h[60050],numw;
void insert(int u,int v,int cost)
{
w[numw].v=v;
w[numw].cost=cost;
w[numw].next=h[u];
h[u]=numw++;
}
queue<int>q;
long long spfa(int s,int t)
{
int v[60050],i;
long long dis[60050];
for(i=0;60050>i;i++)
{
v[i]=0;
dis[i]=10000000000000000;
}
dis[s]=0;
q.push(s);
while(q.empty()==0)
{
s=q.front();
q.pop();
v[s]=0;
for(i=h[s];i!=-1;i=w[i].next)
{
if(dis[w[i].v]>dis[s]+w[i].cost)
{
dis[w[i].v]=dis[s]+w[i].cost;
if(v[w[i].v]==0)
{
v[w[i].v]=1;
q.push(w[i].v);
}
}
}
}
if(dis[t]==10000000000000000)
{
dis[t]=-1;
}
return dis[t];
}
int main()
{
int i,i1,n,m,m1,k,u,v,cost;
scanf("%d %d",&n,&m);
memset(h,-1,sizeof(h));
numw=0;
for(i=0;m>i;i++)
{
scanf("%d",&k);
for(i1=1;k>=i1;i1++)
{
scanf("%d",&u);
insert(i,u-1+2*m,0); //城市羣入口到城市建邊,權值爲0
insert(u-1+2*m,i+m,0); //城市到城市羣出口建邊,權值爲0
}
}
scanf("%d",&m1);
for(i=1;m1>=i;i++) //對於第一類路直接建邊記可
{
scanf("%d %d %d",&u,&v,&cost);
insert(u-1+2*m,v-1+2*m,cost);
insert(v-1+2*m,u-1+2*m,cost);
}
scanf("%d",&m1);
for(i=1;m1>=i;i++) //第二類路
{
scanf("%d %d %d",&u,&v,&cost);
insert(v-1+m,u-1,cost); //城市羣v的出口與城市羣u的入口建邊
insert(u-1+m,v-1,cost); //城市羣u的出口與城市羣v的入口建邊
}
scanf("%d %d",&u,&v);
printf("%lld\n",spfa(u-1+2*m,v-1+2*m));
return 0;
}