[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;
}

posted @ 2018-10-28 22:11  ylsoi  阅读(149)  评论(0编辑  收藏  举报