http://acm.hdu.edu.cn/showproblem.php?pid=6166
题意:
给出一张无向图,给定k个特殊点
求这k个特殊点两两之间的最短路
二进制分组
枚举一位二进制位
这一位为1的放到起点集合
这一位为0的放到终点集合
跑一遍两个集合间的最短路
因为是有向图,反过来再跑一遍
正确性分析:
设最优解是x和y间的最短路
若x和y被分在了两个不同的集合,那么两个集合的最短路就是x和y的最短路
而任意两个点至少有一位二进制不同
所以一定会有x和y分在两个不同集合的时候
#include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 100001 typedef long long LL; int n,k; int front[N],to[N],nxt[N],val[N],tot; int point[N]; struct node { int id; LL dis; node(int id_=0,LL dis_=0):id(id_),dis(dis_){} bool operator < (node p) const { return dis>p.dis; } }; priority_queue<node>q; bool End[N],vis[N]; LL dis[N]; LL ans; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); } } void add(int u,int v,int w) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w; } void dijkstra() { node now; while(!q.empty()) { now=q.top(); q.pop(); if(vis[now.id]) continue; vis[now.id]=true; for(int i=front[now.id];i;i=nxt[i]) if(dis[now.id]+val[i]<dis[to[i]]) { dis[to[i]]=dis[now.id]+val[i]; if(End[to[i]]) ans=min(ans,dis[to[i]]); else q.push(node(to[i],dis[to[i]])); } } } void solve() { int S,bit; for(S=1;(1<<S)-1<k;++S); for(int j=1;j<=S;++j) { bit=1<<j-1; memset(End,false,sizeof(End)); memset(vis,false,sizeof(vis)); memset(dis,63,sizeof(dis)); for(int i=1;i<=k;++i) { if(point[i] & bit) q.push(node(point[i],0)),dis[point[i]]=0; else End[point[i]]=true; } dijkstra(); memset(End,false,sizeof(End)); memset(vis,false,sizeof(vis)); memset(dis,63,sizeof(dis)); for(int i=1;i<=k;++i) { if(!(point[i] & bit)) q.push(node(point[i],0)),dis[point[i]]=0; else End[point[i]]=true; } dijkstra(); } } void clear() { tot=0; memset(front,0,sizeof(front)); ans=1e18; } int main() { int T; int m; int u,v,w; read(T); for(int t=1;t<=T;++t) { clear(); read(n); read(m); while(m--) { read(u); read(v); read(w); add(u,v,w); } read(k); for(int i=1;i<=k;++i) read(point[i]); solve(); printf("Case #%d: %I64d\n",t,ans); } }