【第二十三题】带旋转的数独游戏|dfs(北理工/北京理工大学/程序设计方法与实践/小学期 )

目录

前言

dfs学习

思路

代码修正

源代码

我修正后的代码


前言

说实话这题我弄的也不是很明白,而csdn上只有一份代码@loveumozart,还没多少注释,关键还有很多冗余代码,于是我就做了一些分析。

关键是水是可以水,该学的东西还得学,比如dfs

dfs学习

DFS入门级(模板)_浮生未歇-CSDN博客_dfs

思路

先上神犇@.Loyd的思路,是不是看不懂哈哈,太精炼了。 像我这种连蒟蒻都算不上的只能水过去了。

这题总的思路大致是,先一大行一大行的深搜,每行进行旋转次数的全排列, 只在最后一大列判断是否合法,而且只需要判断一小行即可确定这大行是否合法。

之后就是全部最优解的判断,说实话那一段有点玄学,看到最后看不动了,我躺平了

代码修正

原作的代码写的实在有点冗杂,而且很多毫不相干的,我怀疑有点cv哈哈。

而且命名是真tm阴间啊,没有有意义的名字,可读性极差。

然后我就是删掉了冗杂代码,加了一些自己的,加了一堆注释,重命名了变量,关键函数重写了一遍,虽然看起来有270行,但是估计注释就至少70行。其实是精简了不少。

源代码

20. 带旋转的数独游戏_Jiahua's blog-CSDN博客

我修正后的代码

//库引用与宏定义
#include<stdio.h>
#include<stdbool.h>
#include<string.h>
//函数功能详见函数定义部分
bool row_ok(int u); 
int check_col(void);
void dfs(int u, int v);
void spin(int n, int x, int y);
void back(int x, int y);
//定义全局变量
char maze[18][18];//实际操作的
char backup[18][18];//备份
int steps[4][4];//记录block的翻转次数
char block[4][4];//块
int c[16]; //c用来记录字母出现次数
char x[16];
bool solved;//用于执行连续回退的标记
bool flag;//用于跳出循环
//主函数
int main()
{
//    freopen("input.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T-- > 0)
    {
        //读取数据,存到maze,backup复制maze内容,备份用于恢复maze
        for (int i = 0; i < 16; i++)
        {
            scanf("%s", &maze[i]);
            for (int j = 0; j < 16; j++)
                backup[i][j] = maze[i][j];
        }
        //对1-4大行进行dfs,每一次dfs都从头遍历到尾,对每一块都进行4种翻转状态的枚举
        for (int i = 1; i <= 4; i++)
        {
            //对一行进行深搜之前初始化solved为false
            solved = false;
            dfs(i, 1);
        }
        //走到这里,如果有最优解,maze里就存了,如果没有,那么整行maze都是0
        
        /*int count = 0;
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
                count += steps[i][j];
        printf("%d", count);*/
        /*for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
                printf("%d\t", steps[i][j]);
            putchar('\n');
        }*/
        
        //用列计算最优解
        flag = false;
        for (int i1 = 0; i1 < 2; i1++)
        {
            for (int i2 = 0; i2 < 2; i2++)
            {
                for (int i3 = 0; i3 < 2; i3++)
                {
                    for (int i4 = 0; i4 < 2; i4++)
                    {
                        x[0] = maze[0][0 + i1 * 3];
                        x[1] = maze[1][0 + i1 * 3];
                        x[2] = maze[2][0 + i1 * 3];
                        x[3] = maze[3][0 + i1 * 3];
                        x[4] = maze[4][0 + i2 * 3];
                        x[5] = maze[5][0 + i2 * 3];
                        x[6] = maze[6][0 + i2 * 3];
                        x[7] = maze[7][0 + i2 * 3];
                        x[8] = maze[8][0 + i3 * 3];
                        x[9] = maze[9][0 + i3 * 3];
                        x[10] = maze[10][0 + i3 * 3];
                        x[11] = maze[11][0 + i3 * 3];
                        x[12] = maze[12][0 + i4 * 3];
                        x[13] = maze[13][0 + i4 * 3];
                        x[14] = maze[14][0 + i4 * 3];
                        x[15] = maze[15][0 + i4 * 3];
                        if (check_col() == 1)
                            flag = true;
                        if (flag) break;
                        for (int m = 0; m < 4; m++)
                            steps[3][m] = (steps[3][m] + 2) % 4;
                    }
                    if (flag) break;
                    for (int m = 0; m < 4; m++)
                        steps[2][m] = (steps[2][m] + 2) % 4;
                }
                if (flag) break;
                for (int m = 0; m < 4; m++)
                    steps[1][m] = (steps[1][m] + 2) % 4;
            }
            if (flag) break;
            for (int m = 0; m < 4; m++)
                steps[0][m] = (steps[0][m] + 2) % 4;
        }
        //输出部分
        int total = 0, total1 = 0;
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
            {
                total = steps[i][j] + total;
                total1 = (steps[i][j] + 2) % 4 + total1;
            }
        if (total < total1)
        {
            printf("%d\n", total);
            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 4; j++)
                    for (int k = 0; k < steps[i][j]; k++)
                        printf("%d %d\n", i + 1, j + 1);
        }
        else
        {
            printf("%d\n", total1);
            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 4; j++)
                    for (int k = 0; k < (steps[i][j] + 2) % 4; k++)
                        printf("%d %d\n", i + 1, j + 1);
        }
        
    }

    return 0;
}

//函数定义

//对u-v块进行dfs,搜索完末尾为止
void dfs(int u, int v)
{
    //本dfs进行变动,不再开头设置终止条件,而将终止条件放到别处,用solved实现连续回退
    //我们需要最优解,但是看起来貌似不是最优解,其实不然
    //因为我们是从左往右,按照0000,0001,0002,0003,0010,0011,0012,0013
    //这种方式递增的,所以找到的第一个解就是本大行的最优解
    //这里涉及到dfs的最优解判断,在bfs中,因为是一层一层判断,所以第一次碰到的就是最优解
    //本题就用到了类似的这种思想
    //还有另一种功能dfs判断最优解的方法就是用一个全局变量去和每次新产生的ans比较
    //所有情况经历过后的更优解就是最终的最优解

    if (v == 4)//最后一列的情况,不再向右递归
    {
        for (int i = 0; i < 4; i++) 
        {
            spin(i, u, v);
            //只需要在末端进行一次行判断,而且一次也只需要判断一行即可
            //成功了就打开solved,保留steps,开始连续回退,结束本次dfs
            if (row_ok((u - 1) * 4))
            {
                solved = true;
                return;
            }
            else
            {
                back(u, v);
                steps[u - 1][v - 1] = 0;
            }
        }
    }
    else//一般情况,含有递归,要继续往下搜索
    {
        //对本层遍历四种情况,从不转到转3次
        for (int i = 0; i < 4; i++)
        {
            spin(i, u, v);
            dfs(u, v + 1);
            //检测成功,执行连续回退
            if (solved)
            {
                return;
            }
            //如果检测该行失败,那么就恢复原状,继续进行下一种翻转情况的尝试
            //其实可以不重置steps,下一次的spin函数会重置当前步数
            //但是懒,因为i=3有一点不确定性,就直接加上重置了
            else
            {
                back(u, v);
                steps[u - 1][v - 1] = 0;
            }   
        }
    }
//    return 0;  //都tm走到最后了,还用return?大概只是可读性
}
//检查maze中的一行,u是实际行数
bool row_ok(int u) 
{
    //记录出现的次数,最终个出现一次即可,c用来记录当前行出现的次数
    for (int i = 0; i < 16; i++)
    {
        switch (maze[u][i])
        {
        case '0': c[0]++; break;
        case '1': c[1]++; break;
        case '2': c[2]++; break;
        case '3': c[3]++; break;
        case '4': c[4]++; break;
        case '5': c[5]++; break;
        case '6': c[6]++; break;
        case '7': c[7]++; break;
        case '8': c[8]++; break;
        case '9': c[9]++; break;
        case 'A': c[10]++; break;
        case 'B': c[11]++; break;
        case 'C': c[12]++; break;
        case 'D': c[13]++; break;
        case 'E': c[14]++; break;
        case 'F': c[15]++; break;
        }
    }
    //判断,如果有不符合题意(不是一),就清空计数并返回0
    for (int i = 0; i < 16; i++)
    {
        if (c[i] != 1)
        {
            memset(c, 0, sizeof(c));
            return false;
        }
    }
    //走下来就是无误,那么就返回一个true
    memset(c, 0, sizeof(c));
    return true;
}

int check_col(void) //检查x,x是什么呢?
{
    for (int i = 0; i < 16; i++)
    {
        switch (x[i])
        {
        case '0': c[0]++; break;
        case '1': c[1]++; break;
        case '2': c[2]++; break;
        case '3': c[3]++; break;
        case '4': c[4]++; break;
        case '5': c[5]++; break;
        case '6': c[6]++; break;
        case '7': c[7]++; break;
        case '8': c[8]++; break;
        case '9': c[9]++; break;
        case 'A': c[10]++; break;
        case 'B': c[11]++; break;
        case 'C': c[12]++; break;
        case 'D': c[13]++; break;
        case 'E': c[14]++; break;
        case 'F': c[15]++; break;
        }
    }
    for (int i = 0; i < 16; i++)
    {
        if (c[i] != 1)
        {
            memset(c, 0, sizeof(c));
            return 0;
        }
    }
    memset(c, 0, sizeof(c));
    return 1;
}
//n代表旋转几次0-3,x,y代表大行大列1-4
void spin(int n, int x, int y)
{
    //旋转算法
    for (int i = 0; i < n; i++)
    {
        for (int j = 3; j >= 0; j--)
            for (int k = 0; k < 4; k++)
                block[3 - j][k] = maze[k + (x - 1) * 4][j + (y - 1) * 4];
        for (int jj = 0; jj < 4; jj++)
            for (int kk = 0; kk < 4; kk++)
                maze[jj + (x - 1) * 4][kk + (y - 1) * 4] = block[jj][kk];
    }
    //保存旋转数
    steps[x - 1][y - 1] = n;
}
//通过备份覆盖实现恢复
void back(int x, int y)
{
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
            maze[(x - 1) * 4 + i][(y - 1) * 4 + j] = backup[(x - 1) * 4 + i][(y - 1) * 4 + j];
    }
}

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亦梦亦醒乐逍遥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值