Hdu4712 Hamming Distance ---- 多向BFS

博客介绍了如何利用汉明距离的性质,通过构建超立方体图并进行多向BFS搜索,解决HDU 4712题目中求最小汉明距离的问题。当n较小直接枚举,较大时通过随机算法求解,但作者提出了一种非随机算法,将问题转换为图上两点间最短路径的搜索问题。
摘要由CSDN通过智能技术生成

题目链接:HDU 4712

看评论里面很多人都是用随即算法过的     在这里贴一个非随即算法的解法


题意:给你n个(n<=1e5)数a0~a(n-1)(ai<(1<<20))   要你求这n个数中转化为二进制后任意两个数中最小的汉明距离    \\  wiki百科:汉明距离

例如:a0 = 00000000010000000000   a1 = 00000000000000000001   a2 = 00000000000000000011

则答案为1 , 因为 a1^a2 = 00000000000000000010 其中1的个数为1,则答案为1


思路:

先说下随即算法的思路:首先当n比较小的时候,直接暴力枚举每两个数,求最小的汉明距离即可;当n比较大时,每次直接随即选出两个数a,b,求出汉明距离选取最小的即可。

因为ai<(1<<20),说明最终解一定<=20,解的范围很小,所以随即算法成功的几率还是很高的。

======================================================================================


接下来说说非随即算法(全是个人独立想出的哦微笑

首先要利用汉明距离的一个性质,二进制字符串的汉明距离也等于 n 维超正方体两个顶点之间的曼哈顿距离,其中n 是两个字串的长度。

我们先令(a,b)表示二进制字符a,b的汉明距离!!

怎么解释那个性质呢,就是比如有a,b,c三个二进制字符,其中(a,b)==(b,c)==1,那么(a,c) = (a,b)+(b,c) = 2

再加入一个d,假设(c,d)==1,且(d,a)!=1且(d,b)!=1,那么(d,a) = (a,b)+(b,c)+(c,d) = 3; (d,b) = (b,c)+(c,d) = 2;

(对于这个性质我一开始也是猜测,然后写了个小程序简单验证了一下,再后来仔细看汉明距离的wiki百科的时候才发现上面写着有。。。

怪不得题目上面一开始就表明了(From WIKI)。。。  )


有了这个性质接下来的事情就比较简单了

因为a<(1<<20),所以先把这(1<<20)个数当成(1<<20)个结点,然后把其汉明距离为1的结点连接起来,把边的长度设为1,

这样两个数的汉明距离即为这个图上两点间的最短路长度

如此一来,我们就可以把给出的n个数当成n个起点,然后在图上进行搜索,搜出任意两起点间最短的距离

搜索的方法就类似于多向BFS,具体的实现见代码

PS:多向BFS在搜索时,搜索到一个解并不能马上返回,需要把当前这一层的结点搜索完毕,然后返回一个最优值

比如下面这个图

可以尝试模拟一下,其中1,2,3表示搜索起点,当搜索到4号结点的时候,如果先行搜索红色边的话,则返回值是4,而正确解应该是3



下面贴代码

#include <iostream>
#include <cstring>
#include <stdio.h>
#include <math.h>
#include <fstream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
using namespace std;

#define REP(i,n) for(int i=0;i<(n);i++)
#define FOR(i,j,k) for(int i=j;i<=(k);i++)
#define ll long long
#define base 20
#define maxn (1<<base)+10
/*
ifstream fin("1");
#define cin fin
*/
int a[100009],n;


int Hash(char c){
    if(c>='0'&&c<='9')  return c-'0';
    return 10+c-'A';
}
void Input(int k){
    char s[7];
    cin >> s;
    int st = 0;
    REP(i,5) {
        st *= 16;
        st += Hash(s[i]);
    }
    a[k] = st;
}

int dis[maxn],color[maxn];//dis表示距离,color相当于把从每个起点开始的搜索路径染色
queue <int> q;
int Solve(){
    while(!q.empty())   q.pop();
    memset(color,-1,sizeof(color));
    memset(dis,-1,sizeof(dis));
    REP(i,n){
        if(dis[a[i]] != -1) return 0;
        dis[a[i]] = 0;
        color[a[i]] = i;
        q.push(a[i]);
    }
    int ans = 2e9,floor = 2e9;  // ans 是答案 floor表示的是限定得到解的层数
    while(!q.empty()){
        int u = q.front(); q.pop();
        REP(i,base){
            int v = (u^(1<<i));
            if(dis[v] == -1){
                dis[v] = dis[u] + 1;
                color[v] = color[u];
                // 只有当v的层数小于floor 才将其加入待搜队列
                if(dis[v] <= floor) q.push(v);
            }
            else if(dis[v] != -1){
                if(color[v] == color[u])    continue; // 颜色相同则直接忽略
            //    return dis[v]+dis[u]+1;   直接返回是错误的!!!
                ans = min(ans,dis[v]+dis[u]+1);
                floor = min(floor,dis[u]);
            }
        }
    }
    return ans;
}

int main(){
    int test;
    cin >> test;
    while(test --){
        cin >> n;
        memset(a,-1,sizeof(a));
        REP(i,n)    Input(i);
        cout << Solve() << endl;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值