牛客网 牛客练习赛67 E.牛妹游历城市(二进制建图优化+最短路)

牛妹计划从家出发,通过计算最小行走距离到达目的地。利用Dijkstra算法,结合二进制优化建图,解决特定条件下的路径寻找问题。

题目传送门

牛妹游历城市

最近,牛妹天天宅在家里,真是憋死人了。他决定出去旅游。

牛妹现在正在1号点(自己家里),他决定前往n号点(牛妹想去的地方),中途可以多次经过1~n号点。

现在,已知每个点都有个权值 a i a_i ai,如果 a i & a j ≠ 0 a_i \& a_j ≠0 ai&aj=0,则i号点和j号点之间连有一条双向边,权值为 l o w b i t ( a i & a j ) lowbit(a_i \& a_j) lowbit(ai&aj)

他想要最小化自己的行走距离,但是他计算不出来qaq。相信全牛客最聪明的你一定会吧!

Tips:

&是位运算中and的意思,lowbit(n)的值是最大的 2 x 2^x 2x,满足 2 x ∣ n 2^x | n 2xn

例如 l o w b i t ( 5 ) = l o w b i t ( ( 101 ) 2 ) = 1 ,   l o w b i t ( 8 ) = l o w b i t ( ( 1000 ) 2 ) = 8 lowbit(5)=lowbit((101)_2)=1,\ lowbit(8)=lowbit((1000)_2)=8 lowbit(5)=lowbit((101)2)=1, lowbit(8)=lowbit((1000)2)=8

输入描述:

本题有多组数据。
第一行,输入一个数T,表示数据组数。
接下来2*T行,先输入一个数n,再输入n个数,第i个数表示 a i a_i ai

输出描述:

对于每组数据,输出最小的行走距离。
如果无法从1号点到达n号点,则输出“Impossible”(不含引号)。

示例一
输入
2
6
2 3 5 8 13 21
12
1 2 3 4 5 6 7 8 9 10 11 12
输出
3
5
示例二
输入
5
3
1 2 3
4
177 188 199 211
2
1 2
4
1 1 1 1
5
1 2 4 8 16
输出
1
1
Impossible
1
Impossible

备注:

数据保证 1 ≤ T ≤ 5 , 1 ≤ n ≤ 1 0 5 1≤T≤5,1≤n≤10^5 1T5,1n105, 1 ≤ T ≤ 5 , 1 ≤ n ≤ 1 0 5 , 1 ≤ a i < 2 32 1\le T\le 5, 1\le n\le 10^5, 1\le a_i < 2^{32} 1T5,1n105,1ai<232

思路

建图,然后跑一遍Dijkstra,重点部分在建图的优化:

n 2 n^2 n2建边会T掉,考虑二进制优化建边:

因为 a i < 2 32 a_i<2^{32} ai<232,所以不妨设置每一个数位为一个虚点,连接从 a i a_i ai到虚点的双向边,从 a i a_i ai到虚点的边权为数位对应的基数,从虚点到 a i a_i ai的边权为0

举个例子说明,假如当前有两个点对应的权值为 a 1 = 1 , a 2 = 5 a_1=1,a_2=5 a1=1,a2=5 a 1 a_1 a1对应的二进制表示为1, a 2 a_2 a2对应的二进制表示为101,建边如下图所示,然后以1为起点跑一个 Dijkstra 求dis[n]就可以了。

在这里插入图片描述

代码
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100100
#define INF (ll)0x3f3f3f3f3f3f3f3f
typedef long long ll;
ll arr[MAXN];
struct node{
    ll c;
    int to,nx;
}eages[MAXN*40];
struct point{
    int pos;
    ll dis;
};
bool operator < (point a,point b){
    return a.dis>b.dis;
}
priority_queue<point>q;
int head[MAXN];
int tot;
ll dis[MAXN];
bool vis[MAXN];
ll lowbit(ll x){
    return x&(-x);
}
void init(){
    memset(head,-1,sizeof(head));
    tot=0;
    while (!q.empty()){
        q.pop();
    }
}
void add(int u,int v,ll cost){
    eages[++tot].nx=head[u];
    eages[tot].to=v;
    eages[tot].c=cost;
    head[u]=tot;
}
//普通Dijkstra 
ll dijstra(int s,int n){
    memset(dis,INF,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s]=0;
    int mark;
    ll mi;
    for (int k=0;k<n;k++){
        mi=INF;
        for (int i=0;i<=n;i++){
            if (!vis[i] && dis[i]<mi){
                mi=dis[i];
                mark=i;
            }
        }
        vis[mark]=true;
        for (int i=head[mark];i!=-1;i=eages[i].nx){
            int v=eages[i].to;
            if (dis[mark]+eages[i].c<dis[v]){
                dis[v]=dis[mark]+eages[i].c;
            }
        }
    }
    return dis[n];
}
//堆优化的Dijkstra 
ll dijstra2(int s,int n){
    memset(dis,INF,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s]=0;
    q.push(point{s,0LL});
    while (!q.empty()){
        point t=q.top();
        q.pop();
        int mark=t.pos;
        for (int i=head[mark];i!=-1;i=eages[i].nx){
            int v=eages[i].to;
            if (dis[mark]+eages[i].c<dis[v]){
                dis[v]=dis[mark]+eages[i].c;
                q.push(point{v,dis[v]});
            }
        }

    }
    return dis[n];
}

int main (){
    int cases,n;
    scanf("%d",&cases);
    while(cases--){
        scanf("%d",&n);
        init();
        for (int i=1;i<=n;i++){
            scanf("%lld",&arr[i]);
            ll j=0;
            while (j<=32){
                ll t = (1LL<<j);
                ll c = arr[i] & t;
                if (c){
                    add(i+32,j,t);
                    add(j,i+32,0);
                }
                j++;
            }
        }
        ll ans=dijstra2(1+32,n+32);
        ans==INF?puts("Impossible"):printf("%lld\n",ans);
    }

    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值