首页 > 技术文章 > [蓝桥杯2016初赛]卡片换位

-Ackerman 2020-01-31 13:08 原文

题目链接:http://oj.ecustacm.cn/problem.php?id=1296

 

题目描述

你玩过华容道的游戏吗?这是个类似的,但更简单的游戏。看下面 3 x 2 的格子
    +---+---+---+
    | A | * | * | 
    +---+---+---+
    | B |   | * |
    +---+---+---+
在其中放5张牌,其中A代表关羽,B代表张飞,* 代表士兵。还有一个格子是空着的。
你可以把一张牌移动到相邻的空格中去(对角不算相邻)。
游戏的目标是:关羽和张飞交换位置,其它的牌随便在哪里都可以。

输入

输入存在多组测试数据,对于每组测试数据:
输入两行6个字符表示当前的局面

输出

对于每组测试数据输出一个整数表示答案

样例输入 Copy

* A
**B
A B
***

样例输出 Copy

17
12

 

思路:

题目问最少需要几次,那么很容易想到利用 BFS 去求解

但是这道题相当于一个图的遍历,隐式图的遍历要注意去重的问题,这也是这道题的一个难点。

这里采用开一个数组 去记录 A B 空格三个的位置(因为它要求其他的无所谓),这样就可以完成去重的任务了

然后我们每次都从空格开始走,和普通的 BFS 是一样的

 

#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>
 
#define LL long long
#define INF 0x3f3f3f3f
#define ls nod<<1
#define rs (nod<<1)+1
 
const int maxn = 1e5 + 10 ;
const LL mod = 20010905;
 
char map[10][10];
int row_a,row_b,col_a,col_b,row,col;
int dir[4][2] = {0,1,0,-1,1,0,-1,0};
int vis[3][3][3][3][3][3];
 
struct Node {
    int r,c;
    int ar,ac;
    int br,bc;
    int step;
};
 
 
void bfs() {
    std::queue<Node> q;
    Node now;
    now.r = row;
    now.c = col;
    now.ar = row_a,now.ac = col_a;
    now.br = row_b,now.bc = col_b;
    now.step = 0;
    q.push(now);
    while (!q.empty()) {
        now = q.front();
        q.pop();
        if (vis[now.ar][now.ac][now.br][now.bc][now.r][now.c])
            continue;
        vis[now.ar][now.ac][now.br][now.bc][now.r][now.c] = 1;
        if (now.ar == row_b && now.ac == col_b && now.br == row_a && now.bc == col_a ) {
            printf("%d\n",now.step);
            return ;
        }
        for (int i = 0;i < 4;i++) {
            int x = now.r + dir[i][0];
            int y = now.c + dir[i][1];
            if (x < 0 || x > 1 || y < 0 || y > 2)
                continue;
            Node ed = now;
            ed.r = x,ed.c = y;
            ed.step++;
            if (x == now.ar && y == now.ac) {
                ed.ac = now.c;
                ed.ar = now.r;
            }
            if (x == now.br && y == now.bc) {
                ed.bc = now.c;
                ed.br = now.r;
            }
            if (vis[ed.ar][ed.ac][ed.br][ed.bc][ed.r][ed.c])
                continue;
            q.push(ed);
        }
    }
}
 
int main() {
    while (gets(map[0]) != NULL ) {
        gets(map[1]);
        memset(vis,0, sizeof(vis));
        for (int i = 0; i <= 1; i++) {
            for (int j = 0; j <= 2; j++) {
                if (map[i][j] == ' ') {
                    row = i;
                    col = j;
                }
                if (map[i][j] == 'A') {
                    row_a = i;
                    col_a = j;
                }
                if (map[i][j] == 'B') {
                    row_b = i;
                    col_b = j;
                }
            }
        }
        bfs();
    }
    return 0;
}

 

还有一种 DFS 的写法,大致的想法也是差不多的

 

#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>

using namespace std;
#define inf 0x3f3f3f3f
int minn=inf;
using namespace std;
int go[4][2]= {0,1,0,-1,1,0,-1,0};//方向向量
int vis[3][3][3][3][3][3];//标记数组
struct node
{
    int x;
    int y;
} a,b,k;//分别记录a,b和空格的坐标
void input()//处理输入
{
    char s;
    while((s=getchar())!=EOF){
        for(int i=0; i<2; i++)
        {
            for(int j=0; j<3; j++)
            {
                s=getchar();
                if(s==' ')
                {
                    k.x=i;
                    k.y=j;
                }
                if(s=='A')
                {
                    a.x=i;
                    a.y=j;
                }
                if(s=='B')
                {
                    b.x=i;
                    b.y=j;
                }
            }
            getchar();
        }
    }
}
void dfs(int x1,int y1,int x2,int y2,int x,int y,int step)
{
    if(step>minn)
        return;
    if(x1==b.x&&y1==b.y&&x2==a.x&&y2==a.y)//x1y1到达b x2y2到达a 难道xy是空格?
    {
        minn=step;
        return;
    }
    //判断越界
    if(x<0||x>1||y<0||y>2)
        return;
    if(x1<0||x1>1||y1<0||y1>2)
        return;
    if(x2<0||x2>1||y2<0||y2>2)
        return;
    if(vis[x1][y1][x2][y2][x][y]==1)//三个相对位置被使用过
        return;
    vis[x1][y1][x2][y2][x][y]=1;//标记已使用
    for(int i=0; i<4; i++)
    {
        int xx=x+go[i][0];
        int yy=y+go[i][1];
        if(xx==x1&&yy==y1)//新的一步走到了A处
            dfs(x,y,x2,y2,x1,y1,step+1);//int x1,int y1,int x2,int y2,int x,int y,int step  走到A处,xy和A直接交换位置带入
        else if(xx==x2&&yy==y2)
            dfs(x1,y1,x,y,x2,y2,step+1);//走到B处,xy和B交换带入
        else
            dfs(x1,y1,x2,y2,xx,yy,step+1);//和AB无关则,空格位置改变
    }
    vis[x1][y1][x2][y2][x][y]=0;
}
int main()
{
    char s;
    while((s=getchar())!=EOF) {
        if (s == ' ') {
            k.x = 0;
            k.y = 0;
        }
        if (s == 'A') {
            a.x = 0;
            a.y = 0;
        }
        if (s == 'B') {
            b.x = 0;
            b.y = 0;
        }
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 3; j++) {
                if (i == 0 && j == 0)
                    continue;
                s = getchar();
                if (s == ' ') {
                    k.x = i;
                    k.y = j;
                }
                if (s == 'A') {
                    a.x = i;
                    a.y = j;
                }
                if (s == 'B') {
                    b.x = i;
                    b.y = j;
                }
            }
            getchar();
        }
        minn=inf;
        memset(vis,0, sizeof(vis));
        dfs(a.x, a.y, b.x, b.y, k.x, k.y, 0);
        printf("%d\n", minn);
    }
    return 0;
}

 

推荐阅读