2024年3月29号题解

魔板

解题思路

  1. 如果我们可以固定一个起点,那么我们把所有的走法都枚举了,那么就得到了可以走到的所有解
  2. 但题目给的起点并不是固定的,那么有没有一种函数(方法)可以把任意起点位置转换成一个固定的起点,然后终点也可以转换的呢
  3. 也就是说把一个起点和终点变成固定的其中的对应的终点

例如:

初状态:4 6 2 8 5 7 3 1
末状态:3 4 8 7 2 5 1 6

可以转换成:

初状态:1 2 3 4 5 6 7 8
末状态:7 1 4 6 3 5 8 2

那么我们现在需要的是转换之后的路径等于没有转换的路径,也就是说转换前的状态等于转换后状态。

其实我们只需要对任意起点状态重新进行编号就可以了

即起始状态中

4 ---> 1

6 ---> 2

2 ----> 3

8 ----> 4

5 ----> 5

7 ---->6

3 ----> 7

1 ----> 8

那么终点状态中

3 ----> 7(我们在起始状态中给3重新编号成为了7)

4 ----> 1(我们在起始状态中给4重新编号成为了1)

8 ----> 4(我们在起始状态中给8重新编号成为了4)

7 ----> 6(我们在起始状态中给7重新编号成为了6)

2 ----> 3(我们在起始状态中给2重新编号成为了3)

5 ----> 5(我们在起始状态中给5重新编号成为了5)

1 ----> 8(我们在起始状态中给1重新编号成为了8)

6 ----> 2(我们在起始状态中给6重新编号成为了2)

那么终点状态也需要被一起映射,因为我们要让转换前的状态等于转换后的状态

那么我们只是重新给它们编号,所有转换前的状态等于转换后的状态

那么只需要再使用康托展开来把空间优化一下就可以了

代码实现

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <queue>
#include <set>
#include<unordered_map>
#define sc scanf
#define pr printf
#define Maxn 362880+5//1-9位置最多有9!种情况

using namespace std;

static const int FAC[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };  // 阶乘,用来计算康托值

struct p {
        char s[10];//用来存放魔板的状态
        int hash;//魔板状态对应的hash值
};

p q[Maxn];//队列种最多不会有超过Maxn种状态
bool v[Maxn];//标记有哪些状态是可以到达的,但在这道题用来去重
string path[Maxn];//用来存放从起点到目标状态的路径(操作顺序),因为bfs是按照字典序操作的所以第一次到达一个状态就是最小的字典序
int l = 0;//队列头
int r = 0;//队列尾
char s[10];//用来存放起点的状态
char e[10];//用来存放终点的状态

//康托展开
int cantor(char* a)
{
        int x = 0;
        for (int i = 0; i < 8; ++i)
        {
                int smaller = 0;  // 在当前位之后小于其的个数
                for (int j = i + 1; j < 8; ++j)
                {
                        if (a[j] < a[i])
                                smaller++;
                }
                x += FAC[9 - i - 1] * smaller; // 康托展开累加
        }
        return x + 1;  // 康托展开值
}
//返回进行a操作后的状态信息
p a(p s)
{
        for (int i = 0; i < 4; i++) {//反转字符串就可以了
                swap(s.s[i], s.s[7 - i]);
        }

        s.hash = cantor(s.s);//更新康托值

        return s;
}
//返回进行b操作后的状态信息
p b(p s)
{
        p ans = { 0 };
        //观察一下就可以得到
        ans.s[0] = s.s[3];
        ans.s[1] = s.s[0];
        ans.s[2] = s.s[1];
        ans.s[3] = s.s[2];
        ans.s[4] = s.s[5];
        ans.s[5] = s.s[6];
        ans.s[6] = s.s[7];
        ans.s[7] = s.s[4];

        ans.hash = cantor(ans.s);//更新康托值

        return ans;
}
//返回进行b操作后的状态信息
p c(p s)
{
        p ans = { 0 };
        //直接看样例就可以得到
        ans.s[0] = s.s[0];
        ans.s[1] = s.s[6];
        ans.s[2] = s.s[1];
        ans.s[3] = s.s[3];
        ans.s[4] = s.s[4];
        ans.s[5] = s.s[2];
        ans.s[6] = s.s[5];
        ans.s[7] = s.s[7];

        ans.hash = cantor(ans.s);//更新康托值

        return ans;
}

void bfs()
{       
        p s = { "12345678", 1 };//起点的状态和它对应的康托值

        q[r++] = s;//入队
        v[1] = 1;//把起点状态标记为访问过了
        path[1] = "";//起点不需要任何操作就可以得到

        while (l < r) {
                s = q[l++];//出队

                for (int i = 0; i < 3; i++) {//枚举三种操作
                        p t = s;//用来展示存放
                        switch (i) {
                                case 0: {
                                        t = a(t);//进行a操作

                                        if (!v[t.hash]) {//如果没有到达过
                                                v[t.hash] = 1;//标记已到达
                                                path[t.hash] = path[s.hash] + "A";//之前的路径加当前的操作即A
                                                q[r++] = t;//入队
                                        }
                                        break;
                                }
                                case 1: {
                                        t = b(t);

                                        if (!v[t.hash]) {//如果没有到达过
                                                v[t.hash] = 1;//标记已到达
                                                path[t.hash] = path[s.hash] + "B";//之前的路径加当前的操作即B
                                                q[r++] = t;//入队
                                        }
                                        break;
                                }
                                case 2: {
                                        t = c(t);

                                        if (!v[t.hash]) {//如果没有到达过
                                                v[t.hash] = 1;//标记已到达
                                                path[t.hash] = path[s.hash] + "C";//之前的路径加当前的操作即C
                                                q[r++] = t;//入队
                                        }
                                        break;
                                }
                        }
                }
        }
}

int main() {
        bfs();//得到固定起点的所有可以走到的情况
        while (~sc("%s%s", s, e)) {//读入起点和终点的状态
                char n[256] = "";//用来存放把起始的状态编号成了什么数字
                 
                for (int i = 0; i < 8; i++) {//遍历起点的状态的,给它们重新编号
                        n[s[i]] = i + 1;//存放对应的编号
                        s[i] = i + 1;//进行编号,从1开始
                }

                for (int i = 0; i < 8; i++) {
                        e[i] = n[e[i]];//把终点状态转换
                }

                int f = cantor(e);//计算转换后的康托值

                cout << path[f] << endl;//打印路径
        }

        return 0;
}

速算24点

解题思路

  1. 每次从数组拿两个数进行计算再放入数组直到数组中只有一个数,再判断这个数是不是24就可以了
  2. 在做除法操作的时候还需要判断能不能被整除,和被除数不能为0
  3. 这种从数组中选择数的时候就隐含了一个括号,所以我们不需要枚举括号的位置

代码实现

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <queue>
#include <set>
#include<unordered_map>
#define sc scanf
#define pr printf

using namespace std;

unordered_map<string, int> a;//用来存放数值,把读入的字符串变成对应的数值
string q[4];//读入四个字符串
bool ans;//表示能不能组成24

//一共两个数,四种操作符,但一共有6中不同的组合
int ca(int op, int a, int b) {//op表示什么操作
        if (op == 0 && a && b % a == 0)//被除数不能为0,并且要能够整除因为题目要求不能有小数
                return b / a;
        else if (op == 1)
                return a - b;
        else if (op == 2)
                return b - a;
        else if (op == 3)
                return a * b;//a*b和b*a是一样的
        else if (op == 4 && b && a % b == 0)//被除数不能为0,并且要能够整除因为题目要求不能有小数
                return a / b;

        return a + b;//a+b和b + a是一样的
}

void dfs(int n[], int size) {
        if (ans) {//如果已经找到一种方法可以得到24,那么便不在往下搜索
                return;
        }

        if (size == 1 && n[0] == 24) {//如果数组中只有一个数并且这个数是24代表找到了一个方案可以得到24
                ans = true;//标记为找到了
                return ;
        }
         
        //从数组中选两个数
        for (int i = 0; i < size - 1; i++) {
                for (int j = i + 1; j < size; j++) {
                        int num[20] = { 0 };//新的数组,即把两个数放入组合成一个数在放入数组的数组
                        int nSize = 0;//新数组的大小

                        for (int k = 0; k < size; k++) {//把没有选中的数放入新数组
                                if (k != i && k != j) {
                                        num[nSize++] = n[k];
                                }
                        }

                        for (int k = 0; k < 6; k++) {//遍历6中操作
                                num[nSize++] = ca(k, n[i], n[j]);//新的值放入数组
                                dfs(num, nSize);//进行搜索
                                num[--nSize] = 0;//回溯
                        }
                }
        }
}

void solve(int n[])
{
        ans = false;//每次初始化为false表示不能组成24
        dfs(n, 4);//dfs搜索所有的情况
        if (ans) {//如果ans==true代表找到了,打印Yes
                pr("Yes\n");
        }
        else {
                pr("No\n");
        }
}

int main() {
        a["A"] = 1;
        a["2"] = 2;
        a["3"] = 3;
        a["4"] = 4;
        a["5"] = 5;
        a["6"] = 6;
        a["7"] = 7;
        a["8"] = 8;
        a["9"] = 9;
        a["10"] = 10;
        a["J"] = 11;
        a["Q"] = 12;
        a["K"] = 13;

        while (cin >> q[0] >> q[1] >> q[2] >> q[3]) {
                int n[4] = { 0 };
                if (q[0].size() + q[1].size() + q[2].size() + q[3].size()) {//只要所有字符串大小不都是0
                        //获取对应的值
                        n[0] = a[q[0]];
                        n[1] = a[q[1]];
                        n[2] = a[q[2]];
                        n[3] = a[q[3]];
                        solve(n);//把得到的数值数组进行传递
                }
                else {//表示输入结束
                        break;
                }
        }

        return 0;
}

 

posted @ 2024-03-30 00:18  lwj1239  阅读(4)  评论(0编辑  收藏  举报