算法整理1——全排列问题_二分搜索_递归解决棋盘覆盖问题

1. 全排列问题

问题描述:给你一个数字m,问1~m这几个数字有多少种排列方案,输出每一种排列方案。

解:这是一个全排列问题。解决这个问题可以采用深度优先搜索的思想,即先确定第一个位置的数字(将这个数字加入队列),在第一个数字确定的前提下,递归再确定第二个数字(将这个数字加入队列)……这样,直到确定第m个数字。这就是满足要求的方案中的一种,此时输出整个队列的数字,这也是递归的边界条件。下面给出C++实现代码:

#include<iostream>
using namespace std;
bool vis[19];// 标记数组,用来标记当前数字是否可用
int qu[109];// 用来存储数字的队列
int cnt = 0;
// n代表当前是在确定第几个数字,m代表一共需要确定的数字的个数
void dfs(int n, int m){
	// 递归的边界条件,当你处理的数字位数超过了m,输出队列并返回
    if(n>m){
        for(int i=0;i<cnt;i++)
            cout<<qu[i]<<" ";
        cout<<endl;
        cnt--;
        return ;
    }
    // 从1~m遍历,看看当前数字是不是可以使用
    for(int i=1;i<=m;i++){
        if(vis[i]){
        	// 如果这个数字可用,使用这个数字并将这个数字设置为不可用
            vis[i] = false;
            // 入队
            qu[cnt++] = i;
            // 在确定了当前数字后,递归调用自己,来确定下一个数字
            dfs(n+1, m);
            /*
             这里非常重要,也最不好理解,在后面的数字确定了之后,
             开始返回,此时需要把当前这个数字重新置为true,表示可用。
             举个例子,假设m为4,第一次输出的队列为1234,如果在函数返回
             时,不将4重新置为true,则不会出现1243这种方案,最终结果就
             错了。
            */
            vis[i] = true;
        }
    }
    cnt--;
}
int main(){
	int m;
	// 初始标记数组全设置为true
    for(int i=0;i<19;i++)
        vis[i] = true;
    cin>>m;
    dfs(1, m);
    return 0;
}

2. 二分搜索

问题描述:给你一个有序数组,再给你一个数x,问x是否在这个数组中。

解:实际上,二分搜索的思路是很好理解的。问题的关键在于确保这个数组是有序的(如果可以排序的话即使输入时乱序也没有关系)。在确保数组有序的前提下,我们就可以采用每次比较数组索引中位数和x来缩小解的范围。举个栗子,加入你在玩猜数字游戏,告诉你答案在一到十之间,实际上答案是3,让你猜。你最好的策略就是第一次猜5,告诉你猜大了,之后猜3……也就是每次猜最中间的那个数,这样即使不对,也可以缩小一半的可能区间。因此,如果数组长度是n的话,你最次log2(n)次就一定能得到答案。因此,二分搜索的时间复杂度就是log2(n)。下面给出二分搜索的C++实现代码:

#include<iostream>
#include<algorithm>
using namespace std;
int num[1009];
int main(){
    int n, x;
    bool flag = false;
    // 输入n个数字
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>num[i];
    sort(num, num+n);
    // 输入要判断的数字x
    cin>>x;
    // 进行二分搜索
    int l = 0;// 左指针
    int r = n-1;// 右指针
    // 当左指针大于右指针时就没有必要再进行判断了
    while(l<=r){
        int mid = (l + r) / 2;
        if(x > num[mid])
            l = mid + 1;
        else if(x < num[mid])
            r = mid - 1;
        else{
            cout<<"找到了数字"<<x<<"!"<<endl;
            flag = true;
            break;
        }
    }
    if(!flag)
        cout<<"对不起,未找到数字"<<x<<"!"<<endl;
    return 0;
}

3. 递归解决棋盘覆盖问题

问题描述:给你一个棋盘,其中有一个特殊点(一个小方块),希望你用三个小方块组成的骨牌覆盖这个棋盘。

解:对于这个问题,我们的策略是首先将棋盘分成四个相同的小棋盘,对称分割。之后,对其中没有特殊点的其他三个小棋盘放置骨牌,只需要在最中间放置一个骨牌就可以覆盖三个棋盘。在这里插入图片描述
再然后,递归处理这四个小棋盘就可以了。用这种策略,我们不断缩小了问题的范围(棋盘的大小),直到最终棋盘只有一个格子大小就覆盖完成了。我们用相同的数字标记棋盘上的三个格子来代表一块骨牌的覆盖。C++实现代码如下:

#include<iostream>
using namespace std;
int tile = 1;
int Board[1009][1009];
void ChessBoard(int tr, int tc, int dr, int dc, int Size){
    if(Size == 1) return ;
    int t = tile++, s = Size / 2;

    if(dr < tr + s && dc < tc + s)
        ChessBoard(tr, tc, dr, dc, s);
    else{
        Board[tr + s - 1][tc + s - 1] = t;
        ChessBoard(tr, tc, tr + s - 1, tc + s -1, s);
    }

    if(dr < tr + s && dc >= tc + s)
        ChessBoard(tr, tc + s, dr, dc, s);
    else{
        Board[tr + s - 1][tc + s] = t;
        ChessBoard(tr, tc + s, tr + s - 1, tc + s, s);
    }

    if(dr >= tr + s && dc < tc + s)
        ChessBoard(tr + s, tc, dr, dc, s);
    else{
        Board[tr + s][tc + s - 1] = t;
        ChessBoard(tr + s, tc, tr + s, tc + s -1, s);
    }

    if(dr >= tr + s && dc >= tc + s)
        ChessBoard(tr + s, tc + s, dr, dc, s);
    else{
        Board[tr + s][tc + s] = t;
        ChessBoard(tr + s, tc + s, tr + s, tc + s, s);
    }
}
int main(){
    int tr, tc, dr, dc, Size;
    cout<<"tr=";
    cin>>tr;
    cout<<"tc=";
    cin>>tc;
    cout<<"dr=";
    cin>>dr;
    cout<<"dc=";
    cin>>dc;
    cout<<"Size=";
    cin>>Size;
    ChessBoard(tr, tc, dr, dc, Size);
    cout<<"棋盘覆盖成功!"<<endl;
    for(int i=tr;i<tr+Size-1;i++){
        for(int j=tc;j<tr+Size-1;j++){
            cout<<Board[i][j];
        }
        cout<<endl;
    }
    return 0;
}

PS:此篇博客重点在于给出实现代码,对于问题的描述以及解题思路的讲解可能不够详细全面,敬请谅解。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
时间复杂度(渐近时间复杂度的严格定义,NP问题,时间复杂度的分析方法,主定理)   排序算法(平方排序算法的应用,Shell排序,快速排序,归并排序,时间复杂度下界,三种线性时间排  序,外部排序)   数论(整除,集合论,关系,素数,进位制,辗转相除,扩展的辗转相除,同余运算,解线性同余方程,中国剩余定理) 指针(链表,搜索判重,邻接表,开散列,二叉树的表示,多叉树的表示) 按位运算(and,or,xor,shl,shr,一些应用) 图论(图论模型的建立,平面图,欧拉公式与五色定理,求强连通分量,求割点和桥,欧拉回路,AOV问题,AOE问题,最小生成树的三种算法,最短路的三种算法,标号法,差分约束系统,验证二分图,Konig定理,匈牙利算法,KM算法,稳定婚姻系统,最大流算法,最小割最大流定理,最小费用最大流算法) 计算几何(平面解几及其应用,向量,点积及其应用,叉积及其应用,半平面相交,求点集的凸包,最近点对问题,凸多边形的交,离散化与扫描) 数据结构(广度优先搜索,验证括号匹配,表达式计算,递归的编译,Hash表,分段Hash,并查集,Tarjan算法,二叉堆,左偏树,二斜堆,二项堆,二叉查找树,红黑树,AVL平衡树,Treap,Splay,静态二叉查找树,2-d树,线段树,二维线段树,矩形树,Trie树,块状链表) 组合数学(排列与组合,鸽笼原理,容斥原理,递推,Fibonacci数列,Catalan数列,Stirling数,差分序列,生成函数,置换,Polya原理) 概率论(简单概率,条件概率,Bayes定理,期望值) 矩阵(矩阵的概念和运算,二分求解线性递推方程,多米诺骨牌棋盘覆盖方案数,高斯消元) 字符串处理(KMP,后缀树,有限状态自动机,Huffman编码,简单密码学) 动态规划(单调队列,凸完全单调性,树型动规,多叉转二叉,状态压缩类动规,四边形不等式) 博奕论(Nim取子游戏,博弈树,Shannon开关游戏) 搜索(A*,ID,IDA*,随机调整,遗传算法

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值