[hdu6166]Senior Pan——顶点集合最短路+二进制划分
题目大意:
给定一个有向图和一个顶点的集合,求这些顶点两两构成的最短路的最小值。
思路:
考虑把这个集合给随机划分,最后的答案s->t有很大概率被划分到了两个不同的集合。
正解为二进制划分。
何谓二进制划分?即对于集合中的物品编号,之后枚举其二进制下的每一位,对于每一位,都作一次划分:这一位为1的放入一个集合,这一位为0的放入一个集合。
考虑这样的正确性,两个不同的物品的编号二进制下必有一位不同,所有任意两个物品一定被划分到了两个不同的集合至少一次。
二进制划分之后直接跑Djkstra即可。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define pii pair<ll,int>
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("hdu6166.in","r",stdin);
freopen("hdu6166.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=1e5+10;
const ll inf=1e18;
int T,n,m,qu[maxn];
vector<pii>G[maxn];
ll dis[maxn],ans;
void init(){
read(n); read(m);
int u,v; ll w;
REP(i,1,m){
read(u); read(v); read(w);
G[u].pb(mk(w,v));
}
}
priority_queue<pii,vector<pii>,greater<pii> >h;
void Dijkstra(){
while(!h.empty()){
int u=h.top().se; ll d=h.top().fi;
h.pop();
if(d!=dis[u])continue;
REP(i,0,G[u].size()-1){
int v=G[u][i].se; ll w=G[u][i].fi;
if(d+w<dis[v]){
dis[v]=d+w;
h.push(mk(dis[v],v));
}
}
}
}
void work(){
ans=inf;
int sz;
read(sz);
REP(i,1,sz)read(qu[i]);
REP(i,1,17){
int base=1<<(i-1);
memset(dis,63,sizeof(dis));
REP(j,1,sz)if(qu[j]&base)
dis[qu[j]]=0,h.push(mk(0,qu[j]));
Dijkstra();
REP(j,1,sz)if(!(qu[j]&base))
ans=min(ans,dis[qu[j]]);
memset(dis,63,sizeof(dis));
REP(j,1,sz)if(!(qu[j]&base))
dis[qu[j]]=0,h.push(mk(0,qu[j]));
Dijkstra();
REP(j,1,sz)if(qu[j]&base)
ans=min(ans,dis[qu[j]]);
}
}
int main(){
File();
read(T);
REP(ca,1,T){
init();
work();
printf("Case #%d: %lld\n",ca,ans);
REP(i,1,n)G[i].clear();
}
return 0;
}