HDU 6166 二进制分解 + 最短路

来源:互联网 发布:威尼斯共和国 知乎 编辑:程序博客网 时间:2024/06/01 07:40

题目链接


题意:
n个点m条边的有向图,选出其中k个点成为一个集合,集合中任意选一对跑最短路(不能是自己到自己),问其中距离的最小值是多少。


思路:
首先一个明显的思路就是想办法转化成多起点多终点的最短路,然后建立一个超级起点和超级终点,跑一次得到答案。

但此题起点集和终点集都来自一个集合,所以只能想办法将集合进行分组。

考虑二进制分解,对于每两个不等的数,至少有一个二进制位不一样,这样我们可以枚举每一个二进制位,然后按01 分成两组,再跑最短路,这样答案一定不会有遗漏。

代码:

#include<cmath>#include<queue>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;typedef long long ll;const ll INF = 1e15 + 10;const int A = 1e5 + 10;class Edge{  //存边public:    int u,v;    ll w;}edge[A<<1];class Gra{   //存图public:    int v,next;    ll w;}G[A<<1];ll dis[A];bool vis[A];int head[A],p[A],Belong[A];int tot,n,m,k,Mx;void add(int u,int v,ll w){    G[tot].v = v;    G[tot].w = w;    G[tot].next = head[u];    head[u] = tot++;}void build_G(){        //建图    memset(head,-1,sizeof(head));tot = 0;    for(int i=0 ;i<m ;i++){        int  u = Belong[edge[i].u],v = Belong[edge[i].v];        add(u,v,edge[i].w);    }}ll Spfa(ll st,ll ed){  //跑最短路    queue<ll> que;    for(int i=0;i<=n+1 ;i++){        dis[i] = INF;vis[i] = 0;    }    que.push(st);    dis[st] = 0,vis[st] = 1;    while(que.size()){        int u = que.front();que.pop();        vis[u] = 0;        for(int i=head[u] ;i!=-1 ;i=G[i].next){            int v = G[i].v;ll w = G[i].w;            if(w + dis[u] < dis[v]){                dis[v] = w + dis[u];                if(!vis[v]){                    vis[v] = 1;que.push(v);                }            }        }    }    return dis[ed];}void solve(){    for(int i=0 ;i<=n+1 ;i++) Belong[i] = i;    int S = 0,T = n+1;              //建立超级起点和超级终点    ll ans = INF;    for(int i=0 ;(1<<i)<=Mx ;i++){        for(int j=0 ;j<k ;j++){            if((p[j]>>i)&1) Belong[p[j]] = S;  //分组            else            Belong[p[j]] = T;        }        build_G();        ans = min(ans,Spfa(S,T));        ans = min(ans,Spfa(T,S));    }    printf("%I64d\n",ans);}int main(){    int T,_=1;scanf("%d",&T);    while(T--){        scanf("%d%d",&n,&m);        for(int i=0 ;i<m ;i++){            scanf("%d%d%I64d",&edge[i].u,&edge[i].v,&edge[i].w);        }        scanf("%d",&k);Mx = 0;        for(int i=0 ;i<k ;i++){            scanf("%d",&p[i]);            Mx = max(Mx,p[i]);        }        printf("Case #%d: ",_++);        solve();    }    return 0;}
原创粉丝点击