文章标题 HDU 6166 : Senior Pan(二进制+dijkstra)

Senior Pan

题目链接
题意:有n个点,m条边的有向图,然后从中拿出k个点,然后要我们求出这k个点中距离最短的两个点。
分析:巧妙的用了二进制的思想,对于一个点上的一个二进制位,我们可以知道,不是1就是0,那么我们就可以通过判断这个二进制位是否是1来将这k个点的分成两个集合,由于n的大小只有1e5,所以最多只需要枚举20个二进制为, 然后就是当把k个点分成两个集合后,就是求两个集合之间的最短距离了,求两个集合之间的最短距离就可以用dijkstra来求,其实跟求点到点的最短距离差不多,区别就是一开始先将一个集合的点先放进优先队列,然后当出队的是另一个集合的点,说明最短距离找到了。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <math.h>
#include <vector>
using namespace std;
typedef long long ll;

const int mod=1e9+7;
const int maxn=1e5+10;
const long long inf=1e17;

int n,m,k;
int num[maxn];
int mark[maxn],vis[maxn];

ll dis[maxn];
struct Edge{
    int to,nex,w;
}edge[maxn];

struct node {
    int u;
    ll dis;
    bool operator <(const node &t)const {
        return dis>t.dis;
    }
};
priority_queue<node>q;
int tot,head[maxn];

void addedge(int u,int v,int w){
    edge[tot]=Edge{v,head[u],w};
    head[u]=tot++;
} 
void init(){
    memset (mark,0,sizeof (mark));
    memset (vis,0,sizeof (vis));
    for (int i=0;i<=n;i++)dis[i]=inf;
    while (!q.empty())q.pop();
}
ll dijkstra(){
    while (!q.empty()){
        node tmp=q.top();q.pop();
        int u=tmp.u;
        if (mark[u])return tmp.dis;//如果遇到背标记的一个点,那么就是到达另一个集合的最短距离了 
        if (vis[u])continue;
        vis[u]=true;
        for (int i=head[u];i!=-1;i=edge[i].nex){
            int v=edge[i].to;
            int cost=edge[i].w;
            if (!vis[v]&&dis[v]>dis[u]+cost){
                dis[v]=dis[u]+cost;
                q.push(node{v,dis[v]});
            }
        }
    }
    return inf;
}

ll solve(){
    ll ans=inf;
    for (int i=0;i<20;i++){//通过二进制将k个数的集合分成两个集合,求集合到集合的最短距离 
        init();
        for (int j=0;j<k;j++){
            if (num[j]&(1<<i)){
                mark[num[j]]=1;
            }else {
                dis[num[j]]=0;
                q.push(node{num[j],0});
            }
        }
        ans=min(ans,dijkstra()); 
        init();
        for(int j=0;j<k;j++){//由于是有向图,所以得分两种情况 
            if (num[j]&(1<<i)){
                q.push(node{num[j],0});
            }
            else {
                mark[num[j]]=1;
            }
        }
        ans=min(ans,dijkstra());
    }
    return ans;
}

int main()
{
    int T;
    scanf ("%d",&T);
    int cas=1;
    while (T--){
        scanf ("%d%d",&n,&m);
        tot=0;
        memset (head,-1,sizeof (head));
        int u,v,w;
        for (int i=0;i<m;i++){
            scanf ("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        scanf ("%d",&k);
        for (int i=0;i<k;i++){
            scanf ("%d",&num[i]);
        }
        printf ("Case #%d: %lld\n",cas++,solve());
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值