题意:给你一张带权有向图,问你某个点集中,两两结点之间的最短路的最小值是多少。
其实就是dijkstra,只不过往堆里塞边的时候,要注意塞进去它是从集合中的哪个起始点过来的,然后在更新某个点的答案的时候,如果它是集合中的点,除了最开始入堆的那次以外,要再更新一遍,并且不能用从本身过来的路径进行更新。
std虽然跑了20次dijkstra,但是还是有一些可取之处。
将一个集合中的每个数进行二进制拆分,然后枚举每一位,将该位为0的归入一个半,再将该位为1的归入另一半。这样划分log次,每次只取跨越两半的点对。就必然能覆盖所有点对。
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue> using namespace std; int T,n,m,en,f[100005],head[100005],nxt[100005],to[100005],u,v,vis[100005]; long long dis[100005],d; struct node{ int from,to; long long dis; node() {} node(int from,int to,long long dis):from(from),to(to),dis(dis){ } }; bool operator <(const node &a,const node &b) { return a.dis>b.dis; } priority_queue<node> Q; void add(int u,int v,long long d) { nxt[++en]=head[u]; head[u]=en; to[en]=v; dis[en]=d; } int main() { scanf("%d",&T); for(int TT=1;TT<=T;++TT) { scanf("%d%d",&n,&m); for(int i=1;i<=m;++i) { scanf("%d%d%lld",&u,&v,&d); add(u,v,d); } int t,tt; scanf("%d",&t); for(int i=1;i<=t;++i) { scanf("%d",&tt); f[tt]=1; for(int j=head[tt];j;j=nxt[j]) { Q.push(node(tt,to[j],dis[j])); } } node now; while(!Q.empty()) { now=Q.top(); Q.pop(); if(f[now.to]) { if(now.from==now.to) { } else { printf("Case #%d: %lld ",TT,now.dis); break; } } else { if(vis[now.to]<2) { vis[now.to]++; for(int i=head[now.to];i;i=nxt[i]) { Q.push(node(now.from,to[i],dis[i]+now.dis)); } } } } while(!Q.empty()) Q.pop(); memset(head,0,sizeof head); memset(f,0,sizeof f); memset(vis,0,sizeof vis); en=0; } return 0; }