分析:这道题本质就是很简单的最短路问题,但是如果连边用O(n^2)的暴力会直接TLE掉,连一条边的复杂度是减少不了了,那么能不能减少连边的数量呢?
我们可以设置一个中间点p,假设a中的所有点要到b中去,则从a向p连一条有向边,p向b连一条有向边,可是这样权值不好办啊,那么我们把每个城市圈当作一个中心点,这样从a连向a',a'连向b',b'连向b,除了中间这条边以外的边权值都是0,但是我也有可能从b走向a啊,那么a到a'的两条边的边权都是0,这样会陷入死循环啊?
解决方法很简单,我们把每个中心点拆成两个点,一个点只能进来,一个点只能出去,中心点之间的连边都从出去的点连向进来的点,跑一下最短路就好了.注意要用到long long!
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue> using namespace std; const long long inf = 1e15; int n,m,m1,m2,s,t,head[100010],to[200010],nextt[200010],w[200010],tot,vis[100010]; long long d[100010]; void add(int x,int y,int z) { w[tot] = z; to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } void spfa() { for (int i = 1; i <= n + m + m; i++) d[i] = inf; queue <int> q; q.push(s); d[s] = 0; vis[s] = 1; while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for (int i = head[u]; i + 1; i = nextt[i]) { int v = to[i]; if (d[v] > d[u] + w[i]) { d[v] = d[u] + w[i]; if (!vis[v]) { vis[v] = 1; q.push(v); } } } } } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for (int i = 1; i <= m; i++) { int k; scanf("%d",&k); for (int j = 1; j <= k; j++) { int t; scanf("%d",&t); add(t,i + n,0); add(i + n + m,t,0); } } scanf("%d",&m1); for (int i = 1; i <= m1; i++) { int u,v,c; scanf("%d%d%d",&u,&v,&c); add(u,v,c); add(v,u,c); } scanf("%d",&m2); for (int i = 1; i <= m2; i++) { int a,b,l; scanf("%d%d%d",&a,&b,&l); add(a + n,b + n + m,l); add(b + n,a + n + m,l); } scanf("%d%d",&s,&t); spfa(); if (d[t] == inf) printf("-1 "); else printf("%lld ",d[t]); return 0; }