【C语言】求解数独 求数独的解的个数 多解数独算法

news2024/11/17 4:54:50

目录

什么是数独? 

数独的解法?

数独DFS算法详解

1. 初始化条件

2. 填入已初始化的数独表

3. 填数独

 4. 拓展问题

请问删掉数独中的哪两个数可以使得数独的解最大?

 删除的是哪两个数?

 最终代码

 main函数(如何执行这些代码) 


什么是数独? 

数独的要求是每一行,每一列,每一宫都包括1~9,但是不能有重复数字。


数独的解法?

主流为深度优先搜索算法,如果使用数据结构,有舞蹈链算法,本篇介绍深度优先搜索算法。


数独DFS算法详解

1. 初始化条件

我们的初始条件准备了5个,分别是row[N], col[N], cell[3][3],ones[M], map[M]

N = 9;

M = 111111111(二进制),511(十进制);

//设置9*9数独表
const int N = 9;
//设置mask长度 M的二进制:111111111,从右到左分别表示1 2 3 4 5 6 7 8 9 
const int M = 1 << N;

//row、col、cell分别表示行、列、宫可填写数的编码
//ones、map是一个映射关系,ones表示有多少个1,map表示9位二进制的1代表的数字
int row[N], col[N], cell[3][3];
int ones[M], map[M];

//数独表
int arr[9][9] = {
    4,0,0,9,0,0,0,0,3,
    0,8,0,0,0,1,0,9,0,
    0,0,0,0,2,0,7,0,0,
    0,3,0,0,0,0,0,0,4,
    0,0,6,7,0,0,5,0,0,
    2,0,0,0,0,0,0,6,0,
    0,0,7,0,3,0,6,0,0,
    0,5,0,6,0,0,0,0,0,
    1,0,0,0,0,9,0,0,2
};

那么M是用来干嘛的?

我使用了二进制来优化DFS算法,在下图中只有7不能填,因为mask为0。

    map和ones是一个映射关系,下标(二进制)->值(十进制)
    
    map[10] = 2,意思是二进制为10的数十进制为2 

    ones[11] = 2,意思是二进制为11的数十进制为2

下面初始化的意思是把所有位置都设置成所有数都可填的状态。

//只需一次初始化的数组map、ones
void _init()
{
    //once设置成false后不再执行这个函数
    once = false;

    //map和ones是一个映射关系,下标(二进制)->值(十进制)
    // 
    //map[10] = 2,意思是二进制为10的数十进制为2
    for (int i = 0; i < N; i++)
    {
        map[1 << i] = i + 1;
    }

    //ones[11] = 2,意思是二进制为11的数十进制为2
    for (int i = 0; i < M; i++)
    {
        for (int j = 0; j < N; j++)
        {
            ones[i] += i >> j & 1;
        }
    }
}

//初始化条件数组
int init(int _arr[N][N])
{
    //设置row,col为111111111,代表1`9都在可填写状态
    for (int i = 0; i < N; i++)
    {
        row[i] = col[i] = M - 1;
    }

    //在9个宫中设置值为111111111,代表1`9都在可填写状态
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            cell[i][j] = M - 1;
        }
    }

    //只初始化一次
    if (once)
    {
        _init();
    }

    //填入数独表的已知数字,完成初始化工作。
    return fill(_arr);
}

 fill函数是干嘛的?请往下看


2. 填入已初始化的数独表

fill函数的作用是填入数独表中已知的数字,返回一个整形代表待填入数独表的空位。

我们利用空位作为DFS的制约条件。

//将数组上已知数的位置、值信息做初始化记录,并记录需要填写的格子数
int fill(int _arr[N][N])
{
    //cnt为待填格子数
    int cnt = 0;

    //设置cnt,row、col、cell条件
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            if (!_arr[i][j])
            {
                cnt++;
            }
            else
            {
                col[j] -= 1 << (_arr[i][j] - 1);
                row[i] -= 1 << (_arr[i][j] - 1);
                cell[i / 3][j / 3] -= 1 << (_arr[i][j] - 1);
            }
        }
    }

    return cnt;
}

 在行,列,宫对应的位置减去对应数的二进制码,这样可以把该数字在行、列、宫对应的二进制码设置为0,代表该数字在该行,该列,该宫已经不可以填写。

可举例填写4和6,三个条件的变化,等式右边为二进制码。 

3. 填数独

前置有3个功能函数。

这里说一下getmask

因为&的特点,col & row & cell运算,如果这三个其中一个的二进制码的某个位置上为0,那么返回的计算结果的那个位置的二进制码也为0。

draw是我们递归的灵魂,他的功能是在数组上填数,然后根据填的数修改row、col、cell。

//获得可填数的编码位(截断到最靠右的1) 例如10110 lowbit后得到10 
inline int lowbit(int x)
{
    return x & -x;
}


//获取可填数据 col,row,cell经过位运算可得到一串二进制数字,二进制的1代表可以填进数独的数字
int getmask(int x, int y)
{
    //printBinary(row[x]);
    //std::cout << " ";

    //printBinary(col[y]);
    //std::cout << " ";

    //printBinary(cell[x/3][y/3] );
    //std::cout << " ";

    //printBinary(col[y] & row[x] & cell[x / 3][y / 3]);

    return col[y] & row[x] & cell[x / 3][y / 3];
}


//填数字
void draw(int _arr[9][9], int x, int y, int num, bool is_set)
{
    //如果这个位置已经被填过,那么消除这个位置上的数字
    //如果没有,就设置成num
    if (is_set)
    {
        _arr[x][y] = 0;
    }
    else
    {
        _arr[x][y] = num;
    }

    //将数字num转化成二进制码
    int v = 1 << (num - 1);

    //根据这个位置是否有数字,修改 + - 的逻辑
    if (is_set)
    {
        v = -v;
    }

    // -v 代表此位置行,列,宫的可填数num已经填入,该行,列,宫不可再填num
    row[x] -= v;
    col[y] -= v;
    cell[x / 3][y / 3] -= v;
}

我们 按照标题2. 的逻辑对数独表和三个条件进行增、改,然后搜索。

t_ret表示解的数量。max_ret表示最大解。

位置优化:通过两层循环,找出可填数最少的位置。

//填数独
bool dfs(int _arr[9][9], int cnt, int& t_ret)
{
    //如果可填数为0,则代表已经完成数独
    if (!cnt)
    {
        return true;
    }

    //找出最小可选位置,x、y表示坐标,minv代表可填数
    int minv = 10;
    int x, y;

    //每一个为0的位置都可以通过getmask(x,y)找到一个9位的二进制数,每一个位置上的1都代表对应数字可填
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            //如果状态码state中的1比minv小,则记录下该位置的xy坐标,并记录下最小可填值minv
            if (!_arr[i][j])
            {
                int state = getmask(i, j);
                if (ones[state] < minv)
                {
                    minv = ones[state];
                    x = i, y = j;
                    //std::cout << std::endl;
                    //printBinary(state);
                }
            }
        }
    }

    //拿到状态码
    int state = getmask(x, y);

    //lowbit取到可填数(从小到大),填了就从状态码中消除对应位置上的1
    for (int i = state; i; i -= lowbit(i))
    {
        //拿到二进制对应的十进制数字num
        int num = map[lowbit(i)];
        //填入num
        draw(_arr, x, y, num, false);
        //开始填数,如果已经填完数独,则打印,并记录解的数量t_ret,最大解max_ret
        if (dfs(_arr, cnt - 1, t_ret))
        {
            //print_arr(_arr);

            t_ret++;
            max_ret = t_ret > max_ret ? t_ret : max_ret;
        }
        //撤销填入的num
        draw(_arr, x, y, num, true);
    }

    //如果 i = state 的值是0,那么就代表没有数字可以填的,返回失败,并消除上一位的数字
    return false;
}

 4. 拓展问题

请问删掉数独中的哪两个数可以使得数独的解最大?

 删除的是哪两个数?

函数的逻辑是删除两个数,然后进行DFS,再然后把删除的数填回去,继续删除。

DFS进行之前,我们都初始化row,col,cell三个条件,这样能保证正常递归。

 这里我们使用vector和pair(C++),也就是数组和键值对的数据结构。

first代表x坐标,second代表y坐标。

//得到所有的数组,并记录下数独的最大解
int _getallarr(int tmp[9][9], int& time)
{
    //将每一个已知数字的x,y坐标记录到vii
    std::vector<std::pair<int, int>> vii;
    for (int i = 0; i < 9; i++)
    {
        for (int j = 0; j < 9; j++)
        {
            if (arr[i][j])
            {
                vii.push_back({ i,j });
            }
        }
    }

    //tmp1.tmp2存要删掉的两个数
    int tmp1, tmp2;
    //记录删除的数的坐标
    int max_ret_tmp = max_ret;
    //vpii的每一个元素都是一对坐标,我们只保留2对坐标
    std::vector<std::pair<int, int>> vpii;
    //依次删除两个数,为了保护源数独,把数据传入到tmp中
    for (int i = 0; i < vii.size(); i++)
    {
        for (int j = i + 1; j < vii.size() - 1; j++)
        {
            //存下要删掉的数,搜索完还原。
            tmp1 = tmp[vii[i].first][vii[i].second];
            tmp[vii[i].first][vii[i].second] = 0;

            tmp2 = tmp[vii[j].first][vii[j].second];
            tmp[vii[j].first][vii[j].second] = 0;


            //计算最大解
            int t_ret = 0;
            int cnt = init(tmp);
            dfs(tmp, cnt, t_ret);

            //如果最大解的数值发生变化,那么记录下该点的坐标。
            if (max_ret > max_ret_tmp)
            {
                //此处还可做优化,比如说把2改成time,删time个数的最大解是哪三个?
                max_ret_tmp = max_ret;
                if (vpii.size() == 2)
                {
                    vpii.erase(vpii.begin(), vpii.end());
                }
                vpii.push_back(vii[i]);
                vpii.push_back(vii[j]);
            }

            //还原删除的数
            tmp[vii[i].first][vii[i].second] = tmp1;
            tmp[vii[j].first][vii[j].second] = tmp2;
        }
    }

    std::cout << "删除的坐标是:(" << vpii[0].first << vpii[0].second << ") && (" << vpii[1].first << vpii[1].second << ")" << std::endl;
    return max_ret;
}

//计算最大解
int getMaxRet()
{
    //time为要删的数的个数
    int time = 2;
    //tmp为临时数组
    int tmp[9][9] = { 0 };
    copy_arr(tmp);

    //
    return _getallarr(tmp, time);
}

 最终代码

//设置9*9数独表
const int N = 9;
//设置mask长度 M的二进制:111111111,从右到左分别表示1 2 3 4 5 6 7 8 9 
const int M = 1 << N;

//row、col、cell分别表示行、列、宫可填写数的编码
//ones、map是一个映射关系,ones表示有多少个1,map表示9位二进制的1代表的数字
//max_ret表示数独的最大解
//once 卡关,ones和map数组只需初始化一次 
int row[N], col[N], cell[3][3];
int ones[M], map[M];
int max_ret;
bool once = true;

//数独表
int arr[9][9] = {
    4,0,0,9,0,0,0,0,3,
    0,8,0,0,0,1,0,9,0,
    0,0,0,0,2,0,7,0,0,
    0,3,0,0,0,0,0,0,4,
    0,0,6,7,0,0,5,0,0,
    2,0,0,0,0,0,0,6,0,
    0,0,7,0,3,0,6,0,0,
    0,5,0,6,0,0,0,0,0,
    1,0,0,0,0,9,0,0,2
};

//打印二进制格式(调试用)
void printBinary(int num) 
{
    if (num == 0) {
        std::cout << "0";
        return;
    }

    int binary[32];
    int i = 0;

    while (num > 0) {
        binary[i] = num % 2;
        num /= 2;
        i++;
    }

    for (int j = i - 1; j >= 0; j--) {
        std::cout << binary[j];
    }
}

//获得可填数的编码位(截断到最靠右的1) 例如10110 lowbit后得到10 
inline int lowbit(int x)
{
    return x & -x;
}

//打印数独表
void print_arr(int _arr[9][9])
{
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            std::cout << _arr[i][j];
        }
        std::cout << std::endl;
    }

    std::cout << std::endl;
}

//复制数独表到tmp
void copy_arr(int tmp[][9])
{
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            tmp[i][j] = arr[i][j];
        }
    }

    std::cout << std::endl;
}



//获取可填数据 col,row,cell经过位运算可得到一串二进制数字,二进制的1代表可以填进数独的数字
int getmask(int x, int y)
{
    //printBinary(row[x]);
    //std::cout << " ";

    //printBinary(col[y]);
    //std::cout << " ";

    //printBinary(cell[x/3][y/3] );
    //std::cout << " ";

    //printBinary(col[y] & row[x] & cell[x / 3][y / 3]);

    return col[y] & row[x] & cell[x / 3][y / 3];
}

//填数字
void draw(int _arr[9][9], int x, int y, int num, bool is_set)
{
    //如果这个位置已经被填过,那么消除这个位置上的数字
    //如果没有,就设置成num
    if (is_set)
    {
        _arr[x][y] = 0;
    }
    else
    {
        _arr[x][y] = num;
    }

    //将数字num转化成二进制码
    int v = 1 << (num - 1);

    //根据这个位置是否有数字,修改 + - 的逻辑
    if (is_set)
    {
        v = -v;
    }

    // -v 代表此位置行,列,宫的可填数num已经填入,该行,列,宫不可再填num
    row[x] -= v;
    col[y] -= v;
    cell[x / 3][y / 3] -= v;
}

//将数组上已知数的位置、值信息做初始化记录,并记录需要填写的格子数
int fill(int _arr[N][N])
{
    //cnt为待填格子数
    int cnt = 0;

    //设置cnt,row、col、cell条件
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            if (!_arr[i][j])
            {
                cnt++;
            }
            else
            {
                col[j] -= 1 << (_arr[i][j] - 1);
                row[i] -= 1 << (_arr[i][j] - 1);
                cell[i / 3][j / 3] -= 1 << (_arr[i][j] - 1);
            }
        }
    }

    return cnt;
}

//只需一次初始化的数组map、ones
void _init()
{
    //once设置成false后不再执行这个函数
    once = false;

    //map和ones是一个映射关系,下标(二进制)->值(十进制)
    // 
    //map[10] = 2,意思是二进制为10的数十进制为2
    for (int i = 0; i < N; i++)
    {
        map[1 << i] = i + 1;
    }

    //ones[11] = 2,意思是二进制为11的数十进制为2
    for (int i = 0; i < M; i++)
    {
        for (int j = 0; j < N; j++)
        {
            ones[i] += i >> j & 1;
        }
    }
}

//初始化条件数组
int init(int _arr[N][N])
{
    //设置row,col为111111111,代表1`9都在可填写状态
    for (int i = 0; i < N; i++)
    {
        row[i] = col[i] = M - 1;
    }

    //在9个宫中设置值为111111111,代表1`9都在可填写状态
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            cell[i][j] = M - 1;
        }
    }

    //只初始化一次
    if (once)
    {
        _init();
    }

    //填入数独表的已知数字,完成初始化工作。
    return fill(_arr);
}



//填数独
bool dfs(int _arr[9][9], int cnt, int& t_ret)
{
    //如果可填数为0,则代表已经完成数独
    if (!cnt)
    {
        return true;
    }

    //找出最小可选位置,x、y表示坐标,minv代表可填数
    int minv = 10;
    int x, y;

    //每一个为0的位置都可以通过getmask(x,y)找到一个9位的二进制数,每一个位置上的1都代表对应数字可填
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            //如果状态码state中的1比minv小,则记录下该位置的xy坐标,并记录下最小可填值minv
            if (!_arr[i][j])
            {
                int state = getmask(i, j);
                if (ones[state] < minv)
                {
                    minv = ones[state];
                    x = i, y = j;
                    //std::cout << std::endl;
                    //printBinary(state);
                }
            }
        }
    }

    //拿到状态码
    int state = getmask(x, y);

    //lowbit取到可填数(从小到大),填了就从状态码中消除对应位置上的1
    for (int i = state; i; i -= lowbit(i))
    {
        //拿到二进制对应的十进制数字num
        int num = map[lowbit(i)];
        //填入num
        draw(_arr, x, y, num, false);
        //开始填数,如果已经填完数独,则打印,并记录解的数量t_ret,最大解max_ret
        if (dfs(_arr, cnt - 1, t_ret))
        {
            //print_arr(_arr);

            t_ret++;
            max_ret = t_ret > max_ret ? t_ret : max_ret;
        }
        //撤销填入的num
        draw(_arr, x, y, num, true);
    }

    //如果 i = state 的值是0,那么就代表没有数字可以填的,返回失败,并消除上一位的数字
    return false;
}

//得到所有的数组,并记录下数独的最大解
int _getallarr(int tmp[9][9], int& time)
{
    //将每一个已知数字的x,y坐标记录到vii
    std::vector<std::pair<int, int>> vii;
    for (int i = 0; i < 9; i++)
    {
        for (int j = 0; j < 9; j++)
        {
            if (arr[i][j])
            {
                vii.push_back({ i,j });
            }
        }
    }

    //tmp1.tmp2存要删掉的两个数
    int tmp1, tmp2;
    //记录删除的数的坐标
    int max_ret_tmp = max_ret;
    std::vector<std::pair<int, int>> vpii;
    //依次删除两个数,为了保护源数独,把数据传入到tmp中
    for (int i = 0; i < vii.size(); i++)
    {
        for (int j = i + 1; j < vii.size() - 1; j++)
        {
            tmp1 = tmp[vii[i].first][vii[i].second];
            tmp[vii[i].first][vii[i].second] = 0;

            tmp2 = tmp[vii[j].first][vii[j].second];
            tmp[vii[j].first][vii[j].second] = 0;


            //计算最大解
            int t_ret = 0;
            int cnt = init(tmp);
            dfs(tmp, cnt, t_ret);

            if (max_ret > max_ret_tmp)
            {
                max_ret_tmp = max_ret;
                if (vpii.size() == 2)
                {
                    vpii.erase(vpii.begin(), vpii.end());
                }
                vpii.push_back(vii[i]);
                vpii.push_back(vii[j]);
            }

            //还原删除的数
            tmp[vii[i].first][vii[i].second] = tmp1;
            tmp[vii[j].first][vii[j].second] = tmp2;
        }
    }

    std::cout << "删除的坐标是:(" << vpii[0].first << vpii[0].second << ") && (" << vpii[1].first << vpii[1].second << ")" << std::endl;
    return max_ret;
}

//计算最大解
int getMaxRet()
{
    //time为要删的数的个数
    int time = 2;
    //tmp为临时数组
    int tmp[9][9] = { 0 };
    copy_arr(tmp);

    //
    return _getallarr(tmp, time);
}


 main函数(如何执行这些代码) 

int main()
{
    int t_ret = 0;
    int cnt = init(arr);
    dfs(arr,cnt, t_ret);
    std::cout << max_ret;
    std::cout << getMaxRet();

    return 0;
}

觉得写的不错的话给个三连加关注吧~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1082694.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端-uniapp-开发指南

美团外卖微信小程序开发 uniapp-美团外卖微信小程序开发P1 成果展示P2外卖小程序后端&#xff0c;学习给小程序写http接口P3 主界面配置P4 首页组件拆分P13 外卖列表布局筛选组件商家 布局测试数据创建样式 请求商家外卖数据封装请求并发请求 uni-app框架调用https接口 开发小程…

UE4和C++ 开发-C++绑定widget的方式和初始化UI

C绑定widget的方式有两种&#xff0c;一种是使用meta (BindWidget)&#xff0c;一种是使用GetWidgetFromName(TEXT("")),两种方式都可以。一、meta BindWidget方式 注意这种绑定的方式UMG里面的空间名称需要与C里面声明的变量名称相同 Btn_StartU 二、GetWidge…

成都瀚网科技有限公司:怎么优化抖店体验分?

近年来&#xff0c;抖音电商平台凭借强大的用户基础和广阔的销售渠道吸引了越来越多的商家入驻。然而&#xff0c;对于新手卖家来说&#xff0c;提高抖店经验值却成了一件头疼的事情。那么&#xff0c;如何优化抖店体验分呢&#xff1f;本文将从产品质量、服务态度、运营策略等…

Springboot集成MyBatis实现查询表操作(二)

目录 第一章、准备1.1&#xff09;准备数据库表1.2&#xff09;创建springboot项目&#xff0c;添加依赖1.3&#xff09;使用mybatis逆向工程 第二章、代码开发2.1&#xff09;建包并编写代码2.2&#xff09;application配置文件2.3&#xff09;设置编译位置 第三章、测试访问3…

【NUMA平衡】浅入介绍NUMA平衡技术及调度方式

在云计算方案设计或项目问题处理的时候&#xff0c;经常会遇到NUMA平衡的问题&#xff0c;进行让人不清楚NUMA到底有何用&#xff0c;如何发挥作用&#xff0c;本文就NUMA技术原理和调度进行简要整理&#xff0c;方便后续需要时候查阅学习。 一.背景 一般的对称多处理器中&am…

基于SpringBoot的新闻稿件管理系统

目录 前言 一、技术栈 二、系统功能介绍 管理员模块的实现 用户信息管理 记者信息管理 审批员信息管理 记者模块的实现 新闻信息管理 审批员模块的实现 新闻信息管理 用户模块的实现 新闻信息 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信…

安装JoySSL的SSL证书有什么优势?

近年来&#xff0c;网络安全事件层出不穷&#xff0c;屡禁不止。 据统计仍有57%的网站未进行https加密&#xff0c;成为数据泄漏的“导火索”之一。 而SSL证书不仅仅可以保护网站数据安全&#xff0c;而且可以降低网站被第三方窃取或篡改的风险。 安装JoySSL证书的好处&#…

kaggle新赛:写作质量预测大赛【数据挖掘】

赛题名称&#xff1a;Linking Writing Processes to Writing Quality 赛题链接&#xff1a;https://www.kaggle.com/competitions/linking-writing-processes-to-writing-quality 赛题背景 写作过程中存在复杂的行为动作和认知活动&#xff0c;不同作者可能采用不同的计划修…

振弦采集仪应用于隧道安全监测

振弦采集仪应用于隧道安全监测 振弦采集仪是当今必不可少的现代隧道安全监测工具。该设备广泛应用于隧道内部各种安全参数的实时监测&#xff0c;包括但不限于隧道变形、裂缝、压力、温度等。本文详细介绍了振弦采集仪在隧道安全监测中的应用。 首先&#xff0c;我们来了解一下…

nodejs+vue宠物店管理系统

例如&#xff1a;如何在工作琐碎,记录繁多的情况下将宠物店管理的当前情况反应给管理员决策,等等。在此情况下开发一款宠物店管理系统小程序&#xff0c; 困扰管理层的许多问题当中,宠物店管理也是不敢忽视的一块。但是管理好宠物店又面临很多麻烦需要解决,于是乎变得非常合乎时…

科技资讯|微软AR眼镜新专利曝光,可拆卸电池解决续航焦虑

微软正在深入研究增强现实&#xff08;AR&#xff09;领域&#xff0c;最近申请了一项“热插拔电池”相关专利。该专利于 2023 年 10 月 5 日发布&#xff0c;描述了采用模块化设计的 AR 眼镜&#xff0c;热插拔电池放置在了镜腿部分&#xff0c;可以直接拿下替换&#xff0c;对…

Linux文件目录总结

众所周知&#xff0c;Linux系统文件目录是树状结构&#xff0c;如下图所示&#xff1a; 英文缩写的目录下到底存放的是什么文件&#xff0c;善于做归纳总结的逍遥哥哥来解释一下&#xff1a; /bin&#xff1a;bin是Binary的缩写&#xff0c;这个目录存放着最经常使用的命令。 …

042:mapboxGL点击某feature点,使其为中心点

第042个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中通过鼠标点击某feature点,让其成为中心点。这里用到了click事件和flyTo的方法。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共113行)相关API参…

《设计一款2轮车充电桩系统》

以深圳为例&#xff0c;深圳有400万台电动2轮车&#xff0c;以每个月电费20元计算&#xff0c;深圳每个月用在2轮车充电上的费用为8000万左右。1年10个亿的市场规模。 前景可观&#xff0c;竞争也非常激烈。 本文主要讨论技术实现方案。 方法&#xff1a; 24v/36v直流输出 需…

Linux网络和系统管理

网络管理命令 1、ifconfig 命令 作用 ifconfig 命令用于显示或设置网络设备的信息。格式 ifconfig [网卡名字] [参数]可选项 网卡名字:指定要操作的网络设备。参数: up:启动指定网卡。down:关闭指定网卡。-a:显示所有网卡接口的信息,包括未激活的网卡接口。使用示例 1…

至强服务器BIOS/UEFI驱动开发笔记

至强服务器BIOS/UEFI驱动开发笔记 驱动开发基础Hello UEFI Driver 项目选择项目位置初始化驱动代码文件结构驱动程序入口和基本功能导入AMI工程AMI平台Hello UEFI Driver 编译问题测试结果打印设备列表继续开发`HelloWorldSupported`函数依赖配置使用脚本编译编译测试此DXE驱动…

统计子岛屿的数量

统计子岛屿 题目描述 给你两个 m x n 的二进制矩阵 grid1 和 grid2 &#xff0c;它们只包含 0 &#xff08;表示水域&#xff09;和 1 &#xff08;表示陆地&#xff09;。一个 岛屿 是由 四个方向 &#xff08;水平或者竖直&#xff09;上相邻的 1 组成的区域。任何矩阵以外…

基于SpringBoot的在线试题库系统设计与实现

目录 前言 一、技术栈 二、系统功能介绍 学生管理 教师管理 专业管理 试卷管理 试题管理 考试管理 错题本 考试记录 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 使用旧方法对作业管理信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的…

iPhone 如何强制重启

参考iPhone的官方使用手册 传送门 尤其当 iPhone 未响应&#xff0c;也无法将其关机再开机&#xff0c;此方法最有效&#xff1a; 按住调高音量按钮&#xff0c;然后快速松开。按住调低音量按钮&#xff0c;然后快速松开。按住侧边按钮。当 Apple 标志出现时&#xff0c;松开侧…

ODrive移植keil(五)—— 开环控制和电流变换

目录 一、开环控制1.1、控制原理1.2、硬件接线1.3、代码说明1.4、程序演示1.5、程序架构的体现 二、电流变换2.1、理论说明2.2、代码说明 ODrive、VESC和SimpleFOC 教程链接汇总&#xff1a;请点击 一、开环控制 在SimpleFOC系列中有开环控制的教程&#xff0c;SimpleFOC移植S…