Hdu4712 Hamming Distance ---- 多向BFS

来源:互联网 发布:知乎 用的什么编辑器 编辑:程序博客网 时间:2024/06/05 16:57

题目链接: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;    }}


原创粉丝点击