洛谷 P2730 [USACO3.2] 魔板 Magic Squares

本文介绍了如何通过BFS算法解决一道涉及状态压缩的编程题,包括字符串转化、A、B、C操作的实现以及如何利用哈希表存储字典序最小的操作序列。
摘要由CSDN通过智能技术生成

思路:BFS

是一道比较麻烦的BFS搜索题。

这一道题的难点就在于你怎么对于这行数字进行状态压缩。意思就是把这个数字转化成你能够处理的状态。

讲一下具体的代码思路:

首先,我们习惯于用字符串对于数字进行转化,一开始我们令数字变成字符串,方便我们在之后BFS遍历时使用。

然后,我们可以看到,定义了一个数组cunchu,这个其实就是状态压缩的还原数组,也就是把里面的字符串变成二维数组的形式,也就是题目中说的那种形式。这样方便我们进行题目中的A,B,C操作,可以看到,我们在刚开始的预处理中,首先把字符串变成了cunchu数组也就是sets函数,然后再cunchu数组的基础上进行A,B,C的操作,至于A,B,C操作就不详细讲了,因为是语言基础必练的技巧。

我们操作完之后,需要把这个二维数组再变成字符串,我们就需要压缩了,我们刚刚怎么还原的,那么就倒着来压缩回去。这样就得到了新的字符串get()。

好了,预处理结束之后,我们进入BFS的操作。

前面的pus字符串就和入队坐标一样的操作,至于dist是什么,dist是记录我们的字符串有没有被变换过的数组,也就是相当于在普通问题中走没走过这个路的一种变换。总之就是标记路走没走过。

然后,我们就直接循环了。但是,我们以往的习惯是对于dx,dy进行判断周围,这里需要怎么做呢?来了,m数组定义了,我们取出来队头的字符串,然后有上面三种操作可以选,这样的话就符合我们以前的习惯进行BFS遍历了。

接着就是判断变换之后的字符串有没有遍历过了,没有遍历过,我们就让这个字符串的dist在原先过来的字符串的基础上+1,这样相当于走了一步。

大家会发现,又定义了pre这个东西,这是个啥?也是个哈希表。因为题目中明确说明了我们需要存储字典序最小的一条变化操作,这个东西就是用来存储操作字符的。那么是什么意思呢?这个代表:现在变换之后的字符是由上一个字符经过()操作变化过来的,也就是pre[s]={'A'+0,tmp},tmp就代表我们上一个字符,s就代表经过‘A’+i操作后变换后的字符。这样,整个思路就完事了。

注意:在我们输出操作字符的时候,我们可以从终点往起点推,然后,再让整个字符串进行反转,这样就可以得到操作字符的正常最小字典序了。

上代码:

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<cmath> 
#include<vector>
#include<algorithm>
#include<stack>
#include<queue>
#include<deque>
#include <iomanip>
#include<sstream>
#include<numeric>
#include<map>
#include<limits.h>
#include<unordered_map>
#include<set>
#define int long long
#define MAX 110
#define inf 0x3f3f3f3f
#define _for(i,a,b) for(int i=a;i<(b);i++)
using namespace std;
typedef pair<int, int> PII;
int n, m,k;
int counts;
int dx[] = { 0,1,0,-1};
int dy[] = { 1,0,-1,0 };
int cunchu[2][4];
char bianhao[MAX];
string start, end1;
unordered_map<string, int>dist;
unordered_map<string, pair<char, string>>pre;
queue<string>q;
void sets(string a) {
    for (int i = 0; i < 4; i++) {
        cunchu[0][i] = a[i];
    }
    for (int i = 3, j = 4; i >= 0; i--, j++) {
        cunchu[1][i] = a[j];
    }
}
string get() {
    string res;
    for (int i = 0; i < 4; i++) {
        res += cunchu[0][i];
    }
    for (int i = 3; i >= 0; i--)
        res += cunchu[1][i];
    return res;
}
string A(string a) {
    sets(a);
    for (int i = 0; i < 4; i++) {
        swap(cunchu[0][i], cunchu[1][i]);
    }
    return get();
}
string B(string a) {
    sets(a);
    char tmp1 = cunchu[0][3];
    char tmp2 = cunchu[1][3];
    for (int i = 3; i >= 1; i--) {
        for (int j = 0; j < 2; j++) {
            cunchu[j][i] = cunchu[j][i - 1];
        }
   }
    cunchu[0][0] = tmp1;
    cunchu[1][0] = tmp2;
    return get();
}
string C(string a) {
    sets(a);
    int tmp = cunchu[0][1];
    cunchu[0][1] = cunchu[1][1];
    cunchu[1][1] = cunchu[1][2];
    cunchu[1][2] = cunchu[0][2];
    cunchu[0][2] = tmp;
    return get();
}
int bfs(string start,string end1) {
    q.push(start);
    dist[start] = 0;
    while (!q.empty()) {
        auto tmp = q.front();
        q.pop();
        if (tmp == end1)
            return dist[end1];
        string m[3];
        m[0] = A(tmp);
        m[1] = B(tmp);
        m[2] = C(tmp);
        for (int i = 0; i < 3; i++) {
            string buf = m[i];
            
            if (!dist.count(buf)) {
                dist[buf] = dist[tmp] + 1;
                pre[buf] = { i + 'A',tmp };
                if (buf == end1)
                    return dist[end1];
                q.push(buf);
            }
        }

    }
    return -1;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);
    for (int i = 1; i <= 8; i++) {
        start += i + '0';
    }
    for (int i = 1; i <= 8; i++) {
        int x;
        cin >> x;
        end1 += x + '0';
    }
    int res = bfs(start, end1);
    cout << res << endl;
    string cao;
    while (end1 != start) {
        cao += pre[end1].first;
        end1 = pre[end1].second;
    }
    reverse(cao.begin(), cao.end());
    if (cao.size())
        cout << cao << endl;
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值