BFS的理解和应用

BFS(Breadth First Search)

之前写了DFS理解和应用,这次来写写BFS。

● BFS是方向发散的找,就是1变4,4变16这个意思,类似于分身,不同的分身还可以继续分身。

● 案例解释:找眼镜

          你的眼镜掉在地上以后,你趴在地上找,你总是先摸到最接近你的地方,如果没有找到,再往远一点的地方找找。

● BFS包括单向BFS,双向BFS....

单向(BFS):从起点开始发散寻找,直到遇到终点。

双向(DBFS):起点和终点两个方向同时开始发散寻找,直到两个触角相遇。(emmm这样说应该很形象了吧)

▶▶▶▶ DBFS算法是对BFS算法的一种扩展。对于BFS算法来说,DBFS算法由于采用了从两个点开始扩展的方式,搜索树的深度得到了明显的减少,所以在算法的时间复杂度和空间复杂度上都有较大的优势。另外,因为双向遍历也会出现多种相遇的情况,这时不能直接输出,而是应该检查完该层的所有节点以得到最优值,即交替逐层搜索。也即如果这层搜索遇到了另一个已经访问过的。那么已经搜索过的层数就是答案了,可以跳出来了,以后不会出现更优的了。而当某一边队列空时就无解了。


目录

BFS(Breadth First Search)

Knight Moves  HDOJ - 1372

● BFS

● DBFS

逃离迷宫 HDOJ - 1728

Rescue HDOJ - 1242

挑战ACM迷宫 HNUSTOJ

中国象棋中的跳马问题 HNUSTOJ

八数码问题 HNUSTOJ


以BFS最典型的例子Knight Moves,分别用BFS和DBFS来实现一下。

Knight Moves  HDOJ - 1372

    A friend of you is doing research on the Traveling Knight Problem (TKP) where you are to find the shortest closed tour of knight moves that visits each square of a given set of n squares on a chessboard exactly once. He thinks that the most difficult part of the problem is determining the smallest number of knight moves between two given squares and that, once you have accomplished this, finding the tour would be easy. 
Of course you know that it is vice versa. So you offer him to write a program that solves the "difficult" part. 

Your job is to write a program that takes two squares a and b as input and then determines the number of knight moves on a shortest route from a to b. 

InputThe input file will contain one or more test cases. Each test case consists of one line containing two squares separated by one space. A square is a string consisting of a letter (a-h) representing the column and a digit (1-8) representing the row on the chessboard. 
OutputFor each test case, print one line saying "To get from xx to yy takes n knight moves.". 

不想啃的这里瞄一眼~

题目大意:给出一个棋盘,横坐标a~h,纵坐标1~8,给出两对坐标,问从起点到终点的最少步数。
Sample Input

e2 e4
a1 b2
b2 c3
a1 h8
a1 h7
h8 a1
b1 c3
f6 f6

Sample Output

To get from e2 to e4 takes 2 knight moves.
To get from a1 to b2 takes 4 knight moves.
To get from b2 to c3 takes 2 knight moves.
To get from a1 to h8 takes 6 knight moves.
To get from a1 to h7 takes 5 knight moves.
To get from h8 to a1 takes 6 knight moves.
To get from b1 to c3 takes 1 knight moves.
To get from f6 to f6 takes 0 knight moves.

● BFS

就是发散再发散,直到找到为止,代码如下

​
///BFS--Knight Moves HDOJ 1372
#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>
#include <queue>
#define MAX 100010
using namespace std;

struct position
{
    int x;
    int y;
};
///eight different terminus
position dir[8]={{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2}};
char c[6];///input coordinate
int a[6];///convert to number 
queue<int> que;///用队列存
int vis[9][9];///chessboard
int ans;

int is_right(int a,int b)
{
    if(a>0&&a<=8&&b>0&&b<=8)
        return 1;
    return 0;
}

int BFS()
{
    int col,row,i;
    ans=0;
    que.push(a[0]);
    que.push(a[1]);
    que.push(ans);
    vis[a[1]][a[0]]=1;
    while(!que.empty())
    {
        col=que.front();    que.pop();
        row=que.front();    que.pop();
        ans=que.front();    que.pop();
        if(col==a[2]&&row==a[3])///arrive
            return ans;
        for(i=0;i<8;i++)
        {
            if(is_right(row+dir[i].x,col+dir[i].y)&&
               !vis[row+dir[i].x][col+dir[i].y])
                {
                    que.push(col+dir[i].y);
                    que.push(row+dir[i].x);
                    que.push(ans+1);
                    vis[row+dir[i].x][col+dir[i].y]=1;
                }
        }
    }
}
int main(void)
{
    while(gets(c))
    {
        while(!que.empty())
            que.pop();
        memset(vis,0,sizeof(vis));
        a[0]=c[0]-'a'+1;
        a[1]=c[1]-'0';
        a[2]=c[3]-'a'+1;
        a[3]=c[4]-'0';
        ans=BFS();
        printf("To get from %c%c to %c%c takes %d knight moves.\n",c[0],c[1],c[3],c[4],ans);
    }
    return 0;
}


​

● DBFS

​
///DBFS -- Knight Moves HDOJ 1372
#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>
#include <queue>
#define MAX 100010
using namespace std;

struct position
{
    int x;
    int y;
};
///eight different terminus
position dir[8]={{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2}};
char c[6];///input coordinate
int a[6];///convert to number
queue<int> que1,que2;///定义两个队列
int vis[9][9];///chessboard
int cnt,ans,ans1,ans2;
bool flag;

int is_right(int a,int b)
{
    if(a>0&&a<=8&&b>0&&b<=8)
        return 1;
    return 0;
}

void From_Head_BFS()
{
    int col,row,step,i;
    col=que1.front();    que1.pop();
    row=que1.front();    que1.pop();
    step=que1.front();    que1.pop();
    for(i=0;i<8;i++)
    {
        if(is_right(row+dir[i].x,col+dir[i].y)&&
            vis[row+dir[i].x][col+dir[i].y]!=1)
        {
            ans1=step+1;
            if(vis[row+dir[i].x][col+dir[i].y]==2)///found
            {
                flag=false;
                ans=ans1+ans2;
                return;
            }
            que1.push(col+dir[i].y);
            que1.push(row+dir[i].x);
            que1.push(ans1);
            vis[row+dir[i].x][col+dir[i].y]=1;
        }
    }
}
void From_Tail_BFS()
{
    int col,row,step,i;
    col=que2.front();    que2.pop();
    row=que2.front();    que2.pop();
    step=que2.front();    que2.pop();
    for(i=0;i<8;i++)
    {
        if(is_right(row+dir[i].x,col+dir[i].y)&&
            vis[row+dir[i].x][col+dir[i].y]!=2)
        {
            ans2=step+1;
            if(vis[row+dir[i].x][col+dir[i].y]==1)///found
            {
                flag=false;
                ans=ans1+ans2;
                return;
            }
            que2.push(col+dir[i].y);
            que2.push(row+dir[i].x);
            que2.push(ans2);
            vis[row+dir[i].x][col+dir[i].y]=2;
        }
    }
}
int main(void)
{
    while(gets(c))
    {
        while(!que1.empty())
            que1.pop();
        while(!que2.empty())
            que2.pop();
        memset(vis,0,sizeof(vis));
        a[0]=c[0]-'a'+1;
        a[1]=c[1]-'0';
        a[2]=c[3]-'a'+1;
        a[3]=c[4]-'0';
        flag=true;
        ans=ans1=ans2=0;
        int step=0;
        que1.push(a[0]);
        que1.push(a[1]);
        que1.push(step);
        vis[a[1]][a[0]]=1;

        que2.push(a[2]);
        que2.push(a[3]);
        que2.push(step);
        vis[a[3]][a[2]]=2;

        if(a[0]==a[2] && a[1]==a[3])///起点终点相等,直接输出
            printf("To get from %c%c to %c%c takes 0 knight moves.\n",c[0],c[1],c[3],c[4]);
        else
        {
            while(flag)
            {
                cnt=que1.size()/3;///统计本层结点个数,下面循环实现单层正向BFS扩展
                while(cnt-- && flag)
                    From_Head_BFS();
                cnt=que2.size()/3;///统计本层结点个数,下面循环实现单层反向BFS扩展
                while(cnt-- && flag)
                    From_Tail_BFS();
            }
            printf("To get from %c%c to %c%c takes %d knight moves.\n",c[0],c[1],c[3],c[4],ans);
        }
    }
    return 0;
}


​

逃离迷宫 HDOJ - 1728

Problem Description

  给定一个m × n (m行, n列)的迷宫,迷宫中有两个位置,gloria想从迷宫的一个位置走到另外一个位置,当然迷宫中有些地方是空地,gloria可以穿越,有些地方是障碍,她必须绕行,从迷宫的一个位置,只能走到与它相邻的4个位置中,当然在行走过程中,gloria不能走到迷宫外面去。令人头痛的是,gloria是个没什么方向感的人,因此,她在行走过程中,不能转太多弯了,否则她会晕倒的。我们假定给定的两个位置都是空地,初始时,gloria所面向的方向未定,她可以选择4个方向的任何一个出发,而不算成一次转弯。gloria能从一个位置走到另外一个位置吗?

Input

  第1行为一个整数t (1 ≤ t ≤ 100),表示测试数据的个数,接下来为t组测试数据,每组测试数据中,
  第1行为两个整数m, n (1 ≤ m, n ≤ 100),分别表示迷宫的行数和列数,接下来m行,每行包括n个字符,其中字符'.'表示该位置为空地,字符'*'表示该位置为障碍,输入数据中只有这两种字符,每组测试数据的最后一行为5个整数k, x1, y1, x2, y2 (1 ≤ k ≤ 10, 1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m),其中k表示gloria最多能转的弯数,(x1, y1), (x2, y2)表示两个位置,其中x1,x2对应列,y1, y2对应行。

Output

  每组测试数据对应为一行,若gloria能从一个位置走到另外一个位置,输出“yes”,否则输出“no”。

Sample Input

2 5 5

...**

*.**.

.....

.....

*....

1 1 1 1 3

5 5

...**

*.**.

.....

.....

*....

2 1 1 1 3

Sample Output

no

yes

分析:其实转弯就是步数,如果到了终点而且步数<=k,则输出yes,否则no。

注意有个坑:输入坐标的时候是先纵坐标再横坐标。。我写了半天一直答案不对就是没注意到这个 (눈_눈) 。。。

​///逃离迷宫 HDOJ - 1728
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstring>
using namespace std;
///BFS
struct node
{
    int x,y;
};
char map[209][209];
int used[209][209];
int dir[4][2]={0,1, 0,-1, 1,0, -1,0};
int n,m;
int k,sx,sy,ex,ey;
int judge(int x,int y)
{
    return x>=0&&x<n && y>=0&&y<m && map[x][y]!='*';
}
void BFS(int x,int y)
{
    queue<node> q;
    node now,next;
    now.x=x;
    now.y=y;
    q.push(now);
    bool flag = false;
    while(!q.empty())
    {
        now=q.front();
        q.pop();
        if(now.x==ex && now.y==ey)
        {
            if(used[ex][ey] <= k)
            {
                flag=true;
                printf("yes\n");
            }
            else
                break;
        }
        else
        {
            for(int i=0;i<4;i++)
            {
                next.x = now.x + dir[i][0];
                next.y = now.y + dir[i][1];
                while(judge(next.x,next.y))
                {
                    if(used[next.x][next.y] == -1)
                    {
                        used[next.x][next.y] = used[now.x][now.y] + 1;
                        q.push(next);
                    }
                    next.x += dir[i][0];
                    next.y += dir[i][1];
                }
            }
        }
    }
    if(!flag)
        printf("no\n");
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        for(int i=0;i<n;i++)
            scanf("%s",map[i]);
        scanf("%d%d%d%d%d",&k,&sy,&sx,&ey,&ex);
        sx--,sy--,ex--,ey--;
        memset(used,-1,sizeof(used));
        BFS(sx,sy);
    }
    return 0;
}

​

Rescue HDOJ - 1242

Problem Description

Angel was caught by the MOLIGPY! He was put in prison by Moligpy. The prison is described as a N * M (N, M <= 200) matrix. There are WALLs, ROADs, and GUARDs in the prison.

Angel's friends want to save Angel. Their task is: approach Angel. We assume that "approach Angel" is to get to the position where Angel stays. When there's a guard in the grid, we must kill him (or her?) to move into the grid. We assume that we moving up, down, right, left takes us 1 unit time, and killing a guard takes 1 unit time, too. And we are strong enough to kill all the guards.

You have to calculate the minimal time to approach Angel. (We can move only UP, DOWN, LEFT and RIGHT, to the neighbor grid within bound, of course.)

题目大意:给出一张n*m的地图,给出Angel的位置a和他的朋友的位置r,问他的朋友能不能救Angel(即到达Angel的位置),其中‘#’是墙,‘.’是路,‘x’是警卫,他的朋友可以杀死所有警卫,每次耗时1,每走一步也是耗时1。如果能救出则输出时间,不能则输出那句话。

Input

First line contains two integers stand for N and M.
Then N lines follows, every line has M characters. "." stands for road, "a" stands for Angel, and "r" stands for each of Angel's friend. 
Process to the end of the file.

Output

For each test case, your program should output a single integer, standing for the minimal time needed. If such a number does no exist, you should output a line containing "Poor ANGEL has to stay in the prison all his life." 

Sample Input

7 8 
#.#####. 
#.a#..r. 
#..#x... 
..#..#.# 
#...##.. 
.#...... 
........

Sample Output

13

分析:用优先队列存结果,最小的排前面,不断的pop和比对,最终剩下最小的step。

​
///BFS Rescue HDOJ - 1242 
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstring>
using namespace std;
char map[209][209];
int dir[4][2]={1,0,-1,0,0,-1,0,1};
int n,m;
struct node
{
    int x,y,step;
    friend bool operator<(node a,node b)
    {
        return a.step>b.step;///重载运算符,步数少的排前面
    }
};
int BFS(int x,int y)
{
    priority_queue<node>q;
    node cur,next;
    int i,dx,dy;
    map[x][y]='#';
    cur.x=x;
    cur.y=y;
    cur.step=0;
    q.push(cur);
    while(!q.empty())
    {
        cur=q.top();
        q.pop();
        for(i=0;i<4;i++)///四方扩散,广度搜索
        {
            dx=cur.x+dir[i][0];
            dy=cur.y+dir[i][1];
            if(dx>=0&&dx<n&&dy>=0&&dy<m&&map[dx][dy]!='#')
            {
                next.x=dx;
                next.y=dy;
                next.step=cur.step+1;
                if(map[dx][dy]=='r')///found
                    return next.step;
                else if(map[dx][dy]=='.')
                    q.push(next);
                else
                {
                    next.step++;
                    q.push(next);
                }
                map[dx][dy]='#';///mark
            }
        }
    }
    return -1;
}
int main()
{
    int i,j,ans,sx,sy;
    while(cin>>n>>m)
    {
        for(i=0;i<n;i++)
        {
            scanf("%s",map[i]);
            for(j=0;j<m;j++)
                if(map[i][j]=='a')///locate
                {
                    sx=i;
                    sy=j;
                }
        }
        ans=BFS(sx,sy);
        if(ans==-1)
            printf("Poor ANGEL has to stay in the prison all his life.\n");
        else
            printf("%d\n",ans);
    }
    return 0;
}

​

挑战ACM迷宫 HNUSTOJ

题目描述

如下图所示的是一个由程序设计题目组成的ACM迷宫。迷宫的左上角是入口,右下角是出口。迷宫中每一个格子都有一个程序设计题目,挑战者要AC该题目后才能通过,大于0的数字表示AC该题目所需的最短时间。数字如果是0表示是陷阱,进去了就出不来。现在的问题是:求挑战者从入口到出口所需的最短时间。

输入

有多组测试实例。

对于每组测试实例,先输入一个数字n(1<n<=100),然后输入n*n个数字表示迷宫中的数字。

输出

对应输出挑战者从入口到出口所需的最短时间。

样例输入

10
1 0 2 1 2 3 4 2 2 5
2 1 0 1 3 2 5 7 2 1
1 1 1 0 1 1 1 3 2 3
1 2 1 1 0 1 1 1 2 3
1 1 2 0 2 0 2 3 2 3
2 2 2 2 3 2 0 2 3 2
3 2 2 2 1 1 1 0 1 1
0 1 1 3 0 1 1 2 3 2
2 0 1 1 2 2 2 2 2 2
3 2 3 2 3 2 3 2 3 2
5
1 2 1 2 5
1 3 2 4 5
2 1 0 2 5
2 1 1 2 1
2 4 1 1 2

样例输出

min=29
min=11

分析:上面的都看懂了的话。这个应该没有问题了吧

​
​
///逃离ACM迷宫 HNUSTOJ 
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
///用优先队列存结果
int map[109][109];
int vis[109][109];
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int n;
typedef struct node
{
    int x,y,val;///坐标、走到到这里所用的时间
    bool operator <(const node x)const
    {
        return val > x.val;
    }
}node;

int BFS(int n)
{
    priority_queue<node> q;
    node t;
    int sx,sy,ex,ey,nx,ny;
    sx=sy=1,ex=ey=n;
    t.x=sx, t.y=sy, t.val=map[sx][sy];
    vis[sx][sy]=1;
    q.push(t);
    while(!q.empty())
    {
        t=q.top();
        q.pop();
        if(t.x==ex && t.y==ey)///因为是优先队列存的,所以弹出来第一个满足条件的即为所求
            return t.val;
        for(int i=0;i<4;i++)
        {
            nx=t.x+dir[i][0];
            ny=t.y+dir[i][1];
            if(nx>0&&nx<=n && ny>0&&ny<=n && map[nx][ny]
               && !vis[nx][ny])
            {
                vis[nx][ny]=1;
                q.push((node){nx,ny,t.val+map[nx][ny]});
            }
        }
    }
    return -1;
}
int main()
{
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&map[i][j]);
        memset(vis,0,sizeof(vis));
        printf("min=%d\n",BFS(n));
    }
    return 0;
}

​

​

中国象棋中的跳马问题 HNUSTOJ

题目描述

现在棋盘的大小不一定,由p,q给出,并且在棋盘中将出现障碍物(限制马的行动,与象棋走法相同)

输入

第一行输入n表示有n组测试数据。

每组测试数据第一行输入2个整数p,q,表示棋盘的大小(1<=p,q<=100)。
每组测试数据第二行输入4个整数,表示马的起点位置与终点位置。(位置的取值范围同p,q)
第三行输入m表示图中有多少障碍。
接着跟着m行,表示障碍的坐标。

输出

马从起点走到终点所需的最小步数。
如果马走不到终点,则输入“can not reach!”

样例输入

2
9 10
1 1 2 3
0
9 10
1 1 2 3
8
1 2
2 2
3 3
3 4
1 4
3 2
2 4
1 3

样例输出

1
can not reach!

提示

       此题是一个搜索题,可用DFS或BFS,建议选择BFS(广搜)。一开始把马的起始点加入队列,然后用广搜的思想把此点能到达的其他点加入队列,这里需要一个数组用来记录此点在之前是否已经加入队列,如果加入过队列当中,就不需要再加入了,直到队列里的元素为空,或者搜索到了终点,搜索即停止,然后输出相应答案即可。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

这不是普通的跳马!!还要另外加一个判断拐马脚的数组。。ಥ_ಥ

思路:请参考博客:https://blog.csdn.net/deepseazbw/article/details/75269219

经典的棋盘路障问题,路障有2个作用,

1)路障所占的地方不可以作为出发点和到达点,1个位置。

2)路障可以ban马的行走路线,8个位置

(大家可以自己画个图,理解下。)

马可以跳8个方向,可以定义一个方向数组

position dir[8]={{-2,-1},{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2}};

相应的一个障碍也Ban 8个方向的马的行走路线

bar  cir[8]={{-1,0},{-1,0},{0,1},{0,1},{1,0},{1,0},{0,-1},{0,-1}};

​
///中国象棋中的跳马问题 HNUSTOJ
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;

struct position
{
    int x;
    int y;
};
struct node
{
    int x;
    int y;
    int sum;
};
int vis[109][109];
int barrier[109][109];
position dir[8]={{-2,-1},{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2}};
position cir[8]={{-1,0},{-1,0},{0,1},{0,1},{1,0},{1,0},{0,-1},{0,-1}};
queue<node> que;
int ans,n,m,sx,sy,ex,ey,flag;

int is_right(int a,int b)
{
    if(a>0&&a<=n&&b>0&&b<=m)
        return 1;
    return 0;
}
int BFS()
{
    int row,col,tx,ty;
    ans=flag=0;
    node t,m;
    t.x=sx,t.y=sy,t.sum=ans;
    que.push(t);
    vis[sx][sy]=1;
    while(!que.empty())
    {
        t=que.front();
        que.pop();
        row=t.x,col=t.y;
        if(row==ex && col==ey)
        {
            flag=1;
            ans=t.sum;
            return ans;
        }
        for(int i=0;i<8;i++)
        {
            tx=t.x+dir[i].x;
            ty=t.y+dir[i].y;
            if(!barrier[t.x+cir[i].x][t.y+cir[i].y])
            {///没有障碍
                if(is_right(tx,ty) &&!vis[tx][ty] && !barrier[tx][ty])
                {
                    m.x=tx;
                    m.y=ty;
                    m.sum=t.sum+1;
                    que.push(m);
                    vis[tx][ty]=1;
                }
            }
        }
    }
}
int main(void)
{
    int t,ob,obx,oby;
    cin>>t;
    while(t--)
    {
        while(!que.empty())
            que.pop();
        memset(vis,0,sizeof(vis));
        memset(barrier,0,sizeof(barrier));
        scanf("%d%d%d%d%d%d%d",&n,&m,&sx,&sy,&ex,&ey,&ob);
        for(int i=0;i<ob;i++)
        {
            scanf("%d%d",&obx,&oby);
            vis[obx][oby]=1;
            barrier[obx][oby]=1;
        }
        BFS();
        if(flag==1)
            printf("%d",ans);
        else
            printf("can not reach!");
        if(t)
            printf("\n");
    }
    return 0;
}

​

八数码问题 HNUSTOJ

题目描述

八数码问题,即在一个3×3的矩阵中有8个数(1至8)和一个空格,现在要你从一个状态转换到另一个状态,每次只能移动与空格相邻的一个数字到空格当中,问题是要你求从初始状态移动到目标状态所需的最少步数。如下图所示。

1

2

3

 

1

2

3

8

0

4

 

7

8

4

7

6

5

 

0

6

5

                                       初始状态                                                                                            目标状态

如上图所示的数据以矩阵形式给出。现在给出分别代表初始状态和目标状态的两个3*3的矩阵,请给出两个矩阵相互转化的最少步数。

输入

第1行-第3行:3个用空格分隔的整数,代表初始状态相应位置的数字,0代表空格
第4行-第6行:3个用空格分隔的整数,代表终止状态相应位置的数字,0代表空格

输出

第1行:一个整数,最小转换步数,如不能到相互转化则输出"Impossible"

样例输入

8 0 4
7 6 5
1 2 3
7 8 4
0 6 5

样例输出

2

分析:

▷▶用BFS分散,当前状态的下一状态的多种情况即“多个方向”,需要设置判重函数,如果重复了状态那么就不往这个状态发散(即不再走这条路),这里可以用设置一个全排列的数组判重,效率高但是不太好写,也可以用STL里的set的特性判重,代码容易写但是效率不高(何以双全呐,都不容易嘛,好好提高自己吧 (=v=))

      关于判重的方法

      ▶也可以用生成哈希表数组来判重

      ▶另外,还可以用康托展开运算得到一个数组,原理也是数组判重,非常奇妙的一个思想,有兴趣的看一下

康托展开运算
百科链接:https://baike.baidu.com/item/%E5%BA%B7%E6%89%98%E5%B1%95%E5%BC%80/7968428?fr=aladdin

                                公式▷▶▷▶             X=an(n-1)!+an-1(n-2)!+...+a1*0!
其中,ai为整数,并且0<=ai<i,1<=i<=n。ai的意义参见举例中的解释部分

   康托展开的逆运算
    既然康托展开是一个双射,那么一定可以通过康托展开值求出原排列,即可以求出n的全排  列中第x大排列。
       如n=5,x=96时:
  首先用96-1得到95,说明x之前有95个排列.(将此数本身减去1)用95去除4! 得到3余23,说明有3  个数比第1位小,所以第一位是4.用23去除3! 得到3余5,说明有3个数比第2位小,所以是4,但  是4已出现过,因此是5.用5去除2!得到2余1,类似地,这一位是3.用1去除1!得到1余0,这一位  是2.最后一位只能是1.所以这个数是45321。

-------------可以求出某个组合在其全排列里面的顺序X,也可以根据X推出是哪个排列。
-----------------------------------------------------------------------------------
代码实现:
static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};   // 阶乘
  
康托展开逆运算
void decantor(int x, int n)
{
    vector<int> v;  // 存放当前可选数
    vector<int> a;  // 所求排列组合
    for(int i=1;i<=n;i++)
        v.push_back(i);
    for(int i=m;i>=1;i--)
    {
        int r = x % FAC[i-1];
        int t = x / FAC[i-1];
        x = r;
        sort(v.begin(),v.end());// 从小到大排序
        a.push_back(v[t]);      // 剩余数里第t+1个数为当前位
        v.erase(v.begin()+t);   // 移除选做当前位的数
    }
}

这里就放一下用set判重的BFS吧~

///八数码问题 set判重
#include <iostream>
#include <set>
#include <cstring>
#include <queue>
#include <cstdio>
#define MAXSIZE 1000000
using namespace std;

typedef int state[9];  ///定义状态数组
state st[MAXSIZE],goal;
int dist[MAXSIZE];
set<int> vis;
int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};

int try_to_insert(int s)
{
    int v=0;
    for(int i=0;i<9;i++)
        v = v * 9 + st[s][i];
    if(vis.count(v)) ///重复了,跳出
        return 0;
    vis.insert(v);
    return 1;
}

int BFS()
{
    vis.clear();
    int front = 1,rear = 2;
    while(front < rear)
    {
        state& s = st[front];
        if(memcmp(goal, s, sizeof(s))==0)///找到,返回
            return front;
        int z;
        for(z=0; z<9; z++)///找'0'的位置
            if(!s[z]) break;
        int x = z/3, y = z%3;
        for(int i=0;i<4;i++)///四方发散找
        {
            int tx = x + dir[i][0];
            int ty = y + dir[i][1];
            int tz = tx * 3 + ty;
            if(tx>=0 && tx<3 && ty>=0 && ty<3)
            {
                state& t = st[rear];
                memcpy(&t, &s, sizeof(s));///扩展新状态,将s状态的9个值都复制到t
                t[tz] = s[z];///因为空白格移动,修改t中相关的两个值
                t[z] = s[tz];
                dist[rear] = dist[front] + 1;///更新 新状态的距离值
                if(try_to_insert(rear))///不重复,入队
                    rear++;
            }
        }
        front++;///扩展完毕,修改队首指针,为下一次出队列准备
    }
    return 0;
}

int main()
{
    for(int i=0;i<9;i++)
        scanf("%d",&st[1][i]);
    for(int i=0;i<9;i++)
        scanf("%d",&goal[i]);
    int ans=BFS();
    if(ans > 0)
        printf("%d\n",dist[ans]);
    else
        printf("Impossible\n");
    return 0;
}

 

=======遇到好的题目会回来继续更新======

===如果各位大牛牪犇路过发现问题欢迎反映  (「・ω・)「嘿===

©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页