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

转载于:https://www.cnblogs.com/ylsoi/p/9867596.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值