1. 程式人生 > >2017 計蒜之道 複賽 百度地圖導航(拆點最短路)

2017 計蒜之道 複賽 百度地圖導航(拆點最短路)

題目大意:有n個點,m層城市群,每個城市群有一些城市,然後是一些道路,有的是城市之間的最短路,有的是城市群之間的最短路(城市群間的最短路代表兩個城市群之間的城市之間都可以兩兩通過城市群之間的最短路到達),然後給出城市群的城市有哪些,給出城市之間的最短路和城市群之間的最短路

題目思路:n和m都是2e4,直接暴力做最短路當然不行,這個時候我們可以考慮對每個城市群進行拆點,然後做一遍最短路就好,拆點的時候還是儘量拆成兩個點吧,n+i21n+i2這兩個點在連線的時候add(n+i*2-1,n+j*2,cost)和add(n+j*2-1,n+i*2,cost),這樣去建邊就可以保證雙向邊建立成功了,不然只拆一個點容易造成單向邊的情況,這題最短路可能很大,需要注意一下inf的值

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>

using namespace std;
typedef long long ll;

const int maxn = 6e4+10;
const ll inf = 1e15+5;

int cnt, head[maxn];
ll dis[maxn];

struct
Edge { int v, next; ll w; bool operator < (const Edge &rhs) const { return w > rhs.w; } } e[maxn*4]; void add(int u,int v,int co){ e[cnt].v = v; e[cnt].w = co; e[cnt].next = head[u]; head[u] = cnt++; } void init() { cnt = 0; memset(head, -1, sizeof
(head)); } void dij(int s, int len) { priority_queue<Edge> pq; for (int i = 1; i <= len; i++) dis[i] = inf; bool vis[maxn] = {0}; dis[s] = 0; pq.push((Edge){s, 0, 0}); while (!pq.empty()) { Edge tmp = pq.top(); pq.pop(); if (vis[tmp.v]) continue; vis[tmp.v] = 1; for (int i = head[tmp.v]; ~i; i = e[i].next) { Edge u = e[i]; if (dis[u.v] > dis[tmp.v] + u.w) { dis[u.v] = dis[tmp.v] + u.w; pq.push((Edge){u.v, 0, dis[u.v]}); } } } } int main(){ init(); int n,m; vector<int>v[maxn]; scanf("%d%d",&n,&m); for(int i = 1;i <= m;i++){ int k,x; scanf("%d",&k); while(k--){ scanf("%d",&x); v[i].push_back(x); } } for(int i = 1;i <= m;i++){ for(int j = 0;j < v[i].size();j++){ add(v[i][j],n+i*2,0); add(n+i*2-1,v[i][j],0); } } int m1,m2; scanf("%d",&m1); while(m1--){ int uu,vv,ww; scanf("%d%d%d",&uu,&vv,&ww); add(uu,vv,ww); add(vv,uu,ww); } scanf("%d",&m2); while(m2--){ int uu,vv,ww; scanf("%d%d%d",&uu,&vv,&ww); if(uu > vv) swap(uu,vv); add(uu*2+n,vv*2-1+n,ww); add(vv*2+n,uu*2-1+n,ww); } int s,t; scanf("%d%d",&s,&t); dij(s,n+m*2); if(dis[t] == inf) dis[t] = -1; printf("%lld\n",dis[t]); return 0; }