算法设计与分析 期末复习题2023

一、简答题

什么是算法

广义:解决问题时,按照某种机械步骤一定可以得到的处理过程

狭义:用计算机解决问题的方法及步骤的描述

算法的特征

确定性、有穷性、可行性、0或多个输入、一个或多个输出

算法复杂度分析

事前分析、事后统计

资源划分:时间复杂度、空间复杂度

迭代法的基本思想与解题步骤

  • 基本思想:
    一般用于数值计算,一种不断用变量的旧值递推出新值的解决问题的方法

  • 解题步骤

    • 确定迭代模型
    • 建立迭代关系式,数学模型——>循环不变式
    • 对迭代过程进行控制,确定迭代结束的条件

枚举法的基本思想与解题步骤

  • 基本思想

    枚举法是蛮力策略的一种表现形式。它是根据问题中的条件将可能的情况一一列举出来,逐一尝试从中找出满足问题条件的解。但有时一一列举出的情况数目很大,如果超过了所能忍受的范围,则需要进一步考虑,排除一些明显不合理的情况,尽可能减少问题可能解的列举数目

  • 解题步骤

    • 找出枚举范围:分析问题所涉及的各种情况
    • 找出约束条件:分析问题的解需要满足的条件,并用逻辑表达式表示

分治法的基本思想与解题步骤

  • 基本思想

    将一个难以直接解决的大问题,分割成几个规模较小的相似问题,以便各个击破,分而治之

  • 解题步骤

    • 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
    • 解决: 若子问题规模较小而容易被解决则直接解,否则再继续分解为更小的子问题,直到容易解决
    • 合并:将已求解的各个子问题的解,逐步合并为原问题的解

    有时问题分解后,不必求解所有子问题,于是也就不必做第三步操作。例如折半查找,在判别出问题的解在某一个子问题中后,其他子问题就不必求解了,问题的解就是最后(最小)的子问题的解。分治法的这类应用,又称为“减治法”。

贪心法的基本思想与解题步骤

  • 基本思想

    从问题的某一个初始解出发逐步逼近给定的目标,每一步都作一个不可回溯的决策,尽可能求得最优解。当达到某算法中的某一步不需要再继续前进时,算法停止。以逐步的局部最优,达到最终的全局最优

  • 解题步骤

    • 确定问题的子结构
    • 构建贪婪选择策略,局部最优选择
    • 递归构建,达到全局最优

动态规划法的基本思想、基本要素与解题步骤。

  • 基本思想

    把求解的问题分成许多阶段或多个子问题,然后按顺序求解各子问题。前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。

    由于动态规划解决的问题多数有重叠子问题这个特点,为了减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。

  • 解题步骤

    • 划分阶段:按照问题的时间或空间特征,把问题分为若干个有序阶段。
    • 选择状态:将问题发展到各个阶段时所出现的各种客观情况用不同的状态表示来。满足无后效性。
    • 确定决策并写出状态转移方程:递推——根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。

回溯法的基本思想与解题步骤

  • 基本思想

    在包含问题的所有解的解空间树(或森林)中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时总是先判断该结点是否满足问题的约束条件。如果满足进入该子树,继续按深度优先的策略进行搜索。否则,而是逐层向其祖先结点回溯。其实回溯法就是对隐式图的深度优先搜索算法加约束条件。

  • 解题步骤

    • 确定问题的解空间,解空间应至少包含问题的一个最优解
    • 确定结点的扩展规则,确定易于搜索的解空间结构
    • 深度优先搜索解空间,使用剪枝策略避免无效搜索
      回溯算法从根结点出发,以深度优先的方式搜索整个解空间。这个开始点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处,搜索向纵深向移至一个新结点。这个新结点就成为一个新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。此时,应回溯至最近的一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。

分治法与动态规划的异同

  • 相同点:

    • 问题划分:无论是分治法还是动态规划,都涉及将原始问题划分成更小的子问题来解决
    • 子问题求解:分治法和动态规划都要求解子问题,然后将子问题的解合并起来得到原始问题的解。
  • 不同点:

    • 解决方式:分治法采用自顶向下,将问题划分成互不相交的子问题,然后分别解决这些子问题,并将它们的解合并起来得到原始问题的解。而动态规划则采用自底向上的方式,先解决较小的子问题,再逐步解决更大的子问题,最终得到原始问题的解。
    • 问题依赖关系:在分治法中,子问题之间没有相互依赖关系,它们是相互独立的。而动态规划中的子问题通常具有重叠性质,较大的子问题的解可以通过较小的子问题的解来计算。
    • 解空间的结构:分治法通常会将问题划分成多个规模相同的子问题,并且每个子问题的解空间结构相同。而动态规划则通过递推关系将问题的解空间组织成一个有向无环图,通过计算每个节点的值来得到最终解。
    • 最优子结构性质:动态规划通常需要满足最优子结构性质,即问题的最优解可以通过子问题的最优解来构造。而分治法则没有这个要求,它将问题分解为子问题并独立地解决,不涉及最优解的构造

动态规划算法与贪心算法的异同

  • 相同点:

    • 最优子结构性质:两种算法都依赖于问题具有最优子结构性质,即通过子问题的最优解来构造问题的最优解。
  • 不同点:

    • 问题依赖关系:动态规划中的子问题通常具有重叠性质,较大的子问题的解依赖于较小子问题的解,而贪心算法不具备这种依赖关系,它只考虑当前局部最优解。
    • 解决方式:动态规划通过计算并存储中间结果,逐步构建最终解,即自底向上的方式。而贪心算法每次选择局部最优解,并放弃了对其他选择的考虑无需计算中间结果
    • 最终解保证:动态规划能够保证得到全局最优解,但可能会有较高的时间和空间复杂度。而贪心算法只能保证得到局部最优解,不一定是全局最优解,但通常具有较低的时间和空间复杂度。

适用于贪心法策略解决的问题的特征

贪婪策略适用于面对问题仅考虑当前局部信息便做出决策,前提是“局部最优策略能导致产生全局最优解”,应用不当则不能保证求得问题的最优解,。

适用于动态规划策略解决的问题的特征

具有 3 个性质,最优化原理、无后向性、子问题重叠性质。

  • (1)最优化原理:又称最优子结构性质,是指一个问题的最优解包含其子问题的最优解,或一个最优化策略的子策略总是最优的。
  • (2) 无后向性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说某状态以后的过程不会影响以前的状态,只与当前状态有关,这种特性也被称为无后效性。
  • (3)子问题重叠:也就是说子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。对有分解过程的问题还表现在自顶向下分解问题时,每次产生的子问题并不总是新问题,有些子问题会反复出现多次。这个性质并不是动态规划适用的必要条件但是如果该性质无法满足,动态规划算法同其他算法相比就不具备优势

适用于分治法策略的问题

当求解一个输人规模为n取值又相当大的问题时,用蛮力策略效率一般得不到保证。若问题能满足以下几个条件,就能用分治法来提高解决问题的效率。

(1)能将这n个数据分解成个不同子集合,且得到个子集合是可以独立求解的子问题,其中1<k≤n;

(2)分解所得到的子问题与原问题具有相似的结构,便于利用递归或循环机制;

(3)在求出这些子问题的解之后,就可以推解出原问题的解

说明穷举法和回溯法的联系与区别

  • 相同点:它们都是基于试探的。

  • 区别:

    穷举法:将一个解的各个部分全部生成后,才检查是否满足条件,若不满足,则直接放弃该完整解,然后再尝试另一个可能的完整解,它并没有沿着一个可能的完整解的各个部分逐步回退生成解的过程。

    回溯法:一个解的各个部分是逐步生成的,当发现当前生成的某部分不满足约束条件时,就放弃该步所做的工作,退到上一步进行新的尝试,而不是放弃整个解重来。

二、算法题

编程求1 * 2 * 3 * 4……*n的末尾有多少个0。

分析可知,只要求出这一串数中的5的个数,每一个数中5的个数即可。因为2的个数永远是比5多的(比如从1到10,能够分解出来2的个数是2,4,6,8,10也就是相当于有10个2相乘,而5的个数相当于只有两个因而可知,从1到10相乘的最后结果中末尾只有两个0),所以能够得出结果就是从1到n中的所有数的里面含有的5的个数,就是我们要求得末尾的0的个数。

#include<stdio.h>
int count=0;
void main()
{
    int n,i;
    printf("请输入n的值:\n");
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        find(i);
    }
    printf("从1到%d相乘的结果中末尾0的个数为:%d",n,count);
}
int find(int n)
{
    while(n%5==0)
    {
        n=n/5;
        count+=1;
    }
}

编写算法求当n<=100时,n!的准确值。

下面这道题在打印上有些错误,第一行最后一组数字699263应改为699238

在这里插入图片描述

如果对100!这个数没有什么概念,可以从1!开始找规律:

1! 1
2! 2
3! 6
4! 24
5! 120
6! 720
7! 5040
8! 40320
9! 362880
10! 3628800
11! 39916800
…… ……
100! ?
按照输出格式,每六位分一格

在这里插入图片描述

在这里插入图片描述

#include<stdio.h>
int main()
{
    int a[256],b,d;//数组存储中间乘法结果
    int m,n,i,j,cnt=1,flag=0;
    scanf("%d",&n);//求n的   阶乘
    m=1;//表示占用格子数, 
    a[1]=1;
    
    
    for(i=2;i<=n;i++){//每次循环将2到n依次累乘到结果中
        d=0;//表示进位
        for(j=0;j<m;j++){
            b=a[j]*i+d;//从末尾开始,每6位依次乘以轮到的数
            a[j]=b%1000000;//保存每次结果的低六位
            d=b/1000000;//进位
        }
        if(d!=0){//如果进位不为0
            a[j]=d;//将进位存在高位新格子里面
            m++;//格子数加一,j的循环也可以往后进一步
        }
    }
    
    
    printf("%d!\t=%d\t",n,a[m]);
    //循环打印第一行的剩余4组低位数值,每行输入6个数
    for(i=m-1;i>=m-4;i--)
    {
        printf("%06d\t",a[i]);
    }
    printf("\n");
    //循环打印剩余所有的低位数值,每6个为一行
    for(i=m-5;i>=1;i--)
    {
        printf("%06d\t",a[i]);
        if(cnt%6==0)
            printf("\n");
        cnt++;
    }
    return 0;
}

编写算法求满足以下条件的3位整数n

它是完全平方数,其中又有两位数字相同,如144、676等。

#include<stdio.h>
#include<math.h>
int main()
{
	int a,b,c,n,m; 
	for(n=100;n<=999;n++)
	{
	    m = floor(sqrt(n)+0.5);//注意需要+0。5和取整,m为完全根
		a=n/100;//百位
	    b=n%100/10;//十位
		c=n%10;//个位
		if(m*m==n)
		{
			if((a==b)||(a==c)||(b==c))
			printf("%d\t",n);	
		}
		
}	
	return 0;
} 

利用分治算法求一组数据中第二小的数。

#include <stdio.h>

int findSecondSmallest(int arr[], int start, int end) {
    // 基本情况:当只有一个元素时,返回该元素作为第二小的数
    if (start == end) {
        return arr[start];
    }

    // 当有两个元素时,返回较小的那个作为第二小的数
    if (end - start == 1) {
        return (arr[start] < arr[end]) ? arr[start] : arr[end];
    }

    // 将数据分成两半,递归调用  分治算法  求解左半部分和右半部分的第二小的数
    int mid = (start + end) / 2;
    int left_second_smallest = findSecondSmallest(arr, start, mid);
    int right_second_smallest = findSecondSmallest(arr, mid + 1, end);

    // 比较左右两部分的第二小的数,取较小的那个作为整体的第二小的数
    return (left_second_smallest < right_second_smallest) ? left_second_smallest : right_second_smallest;
}

int main() {
    int n;
    printf("请输入数据的数量:");
    scanf("%d", &n);
    //初始化数组长度
    int arr[n];
    printf("请输入数据:");
    //初始化数组
    for (int i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }

    int second_smallest = findSecondSmallest(arr, 0, n - 1);

    printf("第二小的数为: %d\n", second_smallest);

    return 0;
}

在一个n×m的方格中,m为奇数,放置有n×m个数

如图4-18所示,方格中间的下方有一人,此人可按照5个方向前进但不能越出方格,如图4-19所示。

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KN8DgbOh-1687912355745)(file:///C:/Users/LENOVO/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d4HJU6M1-1687912355746)(file:///C:/Users/LENOVO/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg)]

如果用贪婪算法可能找不到真正的最大和。要找到最大和的前提条件就是能看到整个方格的情况。因此需要采用动态规划的算法实现此问题。

如果我们预设方格的左下角为 (0,0) 的话,那么右上角为(m, n),而人的出发点是([m / 2], 0).

人只能走五个方向的格子,所以能直接到达的点(x, y)为(x - 2, y - 1), (x - 1, y - 1), (x, y - 1), (x + 1, y - 1), (x + 2, y - 1)中的一条路径产生。我们要从这里面挑一条和最大的路径。所以F(x, y) = Max{F(x - 2, y - 1), F(x - 1, y - 1), F(x, y - 1), F(x + 1, y - 1), F(x + 2, y - 1)} +Value(x, y)。

边界条件为:F([m / 2, 0]) = 0, F(x, 0) = -无穷(1 <= x <= m && x != [m / 2])

从顶部第一行第一列看的话,最大和的值为a[1] [n] + maxn(a[1] [n - 1], a[2] [n - 1], a[3] [n - 1]),maxn函数为求三个数中的最大值。

所以由此我们可以推断出状态转移方程a[i] [j] = a[i] [j] + maxn(a[i - 2] [j - 1] …a[i + 2] [j - 1]);

算法设计:
1.阶段划分
此题和数塔问题比较像,对于问题的解决需要划分阶段,而且是自下而上的逐层决策。不同于贪婪策略的决策做出的不是唯一决策,第一步对于第六层的(3+4+5+5+5+5+4+3)=27个数据,做出7次选择:(0与2人不可到达)
对于经过第5五层的-1的路径,在第六层的-1,3中选择3;

对于经过第5五层的7的路径,在第六层的-1,3,4中选择4;

对于经过第5五层的4的路径,在第六层的-1,3,4,12中选择12;

对于经过第5五层的0的路径,在第六层的-1,3,4,12,4中选择12;

对于经过第5五层的7的路径,在第六层的3,4,12,4中选择12;

对于经过第5五层的-5的路径,在第六层的4,12,4中选择12;

对于经过第5五层的6的路径,在第六层的12,4中选择12;

通过这样的方法,就将六界问题变成五阶问题。递推出第六层与第五层的和为:

2,11,12,19,7,18。
用同样的方法可以将五阶变成四阶,·····最后得到的一阶数组就是整个问题的最优解。

2.存储
原始数据的存储,用一个二维数组array1即可;
再用一个二维数组array2存储各个阶段的决策结果;
Array2[i] [j]=max(array2[i] [j],array2[i] [j]+array2[i-1] [k])(k是控制方向的)

代码:

#include<stdio.h>
int main(void)
{
    int array1[6][7]={
            {16,4,3,12,6,0,3},
            {4,-5,6,7,0,0,2},
            {6,0,-1,-2,3,6,8},
            {5,3,4,0,0,-2,7},
            {-1,7,4,0,7,-5,6},
            {0,-1,3,4,12,4,2}
           };
    int b[6][7],c[6][7];
    int i,j,k;
    int max;
    int flag;
 	//赋初值
    for(i=0;i<6;i++)
        for(j=0;j<7;j++)
        {
            b[i][j]=array1[i][j];
            c[i][j]=-1;
        }
    //处理第二行到倒数第二行,处理第二行时会递归到第一行
    for(i=1;i<5;i++)
    {
        for(j=0;j<7;j++)
        {
            max=0;
            for(k=j-2;k<=j+2;k++)//每一格对应下面的五格路径
            {
                if(k<0) continue;//五格最左边
                else if(k>6) break;//五格最右边
                    else if(b[i][j]+b[i-1][k]>max)//最大值刷新
                        {
                            max=b[i][j]+b[i-1][k];
                            flag=k;//当前位置选择的上一层路径
                        }
            }
            b[i][j]=max;
            c[i][j]=flag;
        }
    }
 	//特殊处理最后一行
    //没有下一行的元素可供选择,只能在当前行中进行选择,所以人到的只有中间五个格子
    for(j=1;j<=5;j++)//i=5
    {
        max=0;
        for(k=j-2;k<=j+2;k++)
        {
            if(k<0) continue;
            else if(k>6)  break;
            else if(b[i][j]+b[i-1][k]>max)
                    {
                        max=b[i][j]+b[i-1][k];
                        flag=k;
                    }
        }
        b[i][j]=max;
        c[i][j]=flag;
    }
 
    max=0;
    for(j=1;j<=5;j++)   //找出
    {
        if(b[i][j]>max)    
        {
            max=b[i][j];
            flag=j;
        }
    }
    printf("从底到顶最大和值为:%d\n\n",max);
    printf("从底到顶分别取数:");
    printf("%d",array1[i][flag]); 
    for(j=i;j>0;j--)
    {
        flag=c[j][flag];
        printf("%5d",array1[j-1][flag]);
    }
    printf("\n");
    return 0;
}

百马百担问题

有100匹马,驮100担货。大马驮3担,中马驮2担两匹小马驮1担,问大、中、小马各多少?

#include <stdio.h>
int main()
{
	int a,b,c,sum;
    for (a=0;a<=33;a++)
    {
    	for(b=50;b>=0;b--)
    	{
    		c=100-a-b;//小马数量
    		if (c%2!=0)//小马是两马驮一货,故小马的数量为双数
				continue;
    		sum=3*a+2*b+c/2;
    		if(sum==100)
			printf("大马%d,中马%d,小马%d\n",a,b,c);
		}
    }
	return 0;
}

有个小孩正在上楼梯,楼梯有n阶台阶

小孩一次可以上1阶、2阶或3阶,设计算法计算小孩上n阶台阶有多少种上楼梯的方式。

#include<iostream> 
using namespace std; 
const int MAX=1000;

//递归
int countWaysDP(int n, int dp[]) {
    if (n<0) 
    return 0; 
    if (n==0) 
    return 1; 
    if (dp[n]>0) 
    	return dp[n]; //如果大于0 说明这个子问题已经计算过,直接调用数组 
    else { 
        //否则 还需计算该数组, 递归调用 
        dp[n]=countWaysDP(n-1,dp)+countWaysDP(n-2,dp)+countWaysDP(n-3,dp); 
        return dp[n]; 
    } 
} 

int main() { 
    int m[MAX]={0}; 
     // int m[MAX]; 
   for(int i=1;i<10;i++) 
   		cout<<countWaysDP(i,m)<<endl; 
  }

//循环
int countWaysDP(int n, int dp[]) {
    dp[0] = 1; // 初始值,表示爬 0 级楼梯的方式数量为 1
    dp[1] = 1; // 初始值,表示爬 1 级楼梯的方式数量为 1
    dp[2] = 2; // 初始值,表示爬 2 级楼梯的方式数量为 2

    for (int i = 3; i <= n; i++) {
        dp[i] = dp[i-1] + dp[i-2] + dp[i-3];
    }

    return dp[n];
}

int main() { 
    int m[MAX] = {0}; 
    for (int i = 1; i < 10; i++) {
        cout << countWaysDP(i, m) << endl; 
    }
}

求(a+b)^n的系数

在这里插入图片描述

img

题目规定相当明确,要用递归。那就主要思考一下递归体和递归出口(其实这也在问题描述中给出来了)。我是先在纸上画一下样例情况下是怎么个流程,发现只需要将不断减小到k=0或k=n的C的个数加起来就行。如果还有些不理解的话请看如下代码。

#include<stdio.h>
int count;//数一下k=0或k=1个数 

int c(int a,int b)
{
	if(a==0||a==b)//递归出口,k=0或k=n 
	{
		count++;//每出去一个,个数加一 
		//这行加个return也是可以的,看着也许会更舒服些~ 
	} 
	else
	{
		return c(a,b-1)+c(a-1,b-1);//根据题意,返回k~n-1和k-1~n-1 
	}
}

int main()
{
	int n,k;
	scanf("%d%d",&k,&n);
	count=0;//个数初始化为0 
	c(k,n);//进行递归运算 
	printf("%d",count);
	return 0;
}

不求和只求系数

#include <stdio.h>
#include <math.h>

int binomialCoefficient(int n, int k) {
    if (k == 0 || k == n) {
        return 1;
    } else {
        return binomialCoefficient(n - 1, k - 1) + binomialCoefficient(n - 1, k);
    }
}

int main() {
    int a, b, n;

    printf("Enter the values of a, b, and n: ");
    scanf("%d %d %d", &a, &b, &n);

    for (int k = 0; k <= n; k++) {
        int coefficient = binomialCoefficient(n, k);
        int term = coefficient * pow(a, n - k) * pow(b, k);
        printf("Coefficient of (a^%d)*(b^%d): %d\n", n - k, k, term);
    }

    return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u5h9bnNR-1687912355747)(C:\Users\LENOVO\AppData\Roaming\Typora\typora-user-images\image-20230627000732820.png)]

活动安排问题求解

假设某社团某一天要组织n个活动E={1,2,3…n},其中每个活动都要求使用同一礼堂,而且在同一时间内只有一个活动能使用这个礼堂。每个活动i都有一个要求使用礼堂的起始时间si和结束时间fi, 且有si<fi,。若区间(si,fi,)和(sj,fj,)不相交,则称活动i与活动j是相容的。现在给定n个活动的开始时间和结束时间,请设计一个活动安排方案,使得安排的相容活动数目最多

#include<iostream>
using namespace std;

//选择排序算法,对活动序列排序
void sort(int num, int start[], int finish[])
{
    int a, b;
    for(int i = 1; i <= num; ++i)
    {
        for (int j = i + 1; j <= num; ++j)
        {
            if(finish[i] > finish[j])//按结束时间升序排序
            {
                a = start[i]; start[j] = start[i]; start[i] = a;
                b = finish[i]; finish[j] = finish[i]; finish[i] = a;
            }
        }
    }
}

//双指针实现贪心算法
int GreedySelect(int num, int start[], int finish[], bool isArrange[])
{
    isArrange[1] = true;
    int j = 1;//指向前一次已经安排好的活动序号
    int count = 1;//先安排上第一个活动
    for(int i = 2; i <= num; ++i)
    {
        if(finish[j] < start[i])//两活动不相容
        {
            isArrange[i] = true;
            j = i;
            count++;
        }
        else
        {
            isArrange[i] = 0;
        }
        
    }
    return count;//函数模块里不能用cout
}

//主函数
int main()
{
    system("chcp 65001");
    int num, start[50], finish[50];
    bool isArrange[50];
    cout << "活动总数目为:";
    cin >> num;
    cout << "请输入活动的开始时间和结束时间:" << endl;
    for(int i = 1; i <= num; ++i)
    {
        cin >> start[i] >> finish[i];
    }
    sort(num, start, finish);//排序
    cout << "按照结束时间将活动排序的结果为:" << endl;
    for(int i = 1; i <= num; ++i)
    {
        cout << start[i] << "," << finish[i] << endl;
    }
    cout << "被安排活动的个数为:" << GreedySelect(num, start, finish, isArrange) << endl;
    cout << "活动安排的具体情况:" << endl;
    for(int i = 1; i <= num; ++i)
    {
        if(isArrange[i])//isArrange的作用
        {
            cout << start[i] << "," << finish[i] << endl;
        }
    }
    return 0;
}

象棋中马遍历棋盘的问题

在n*m的棋盘中,马只能走日字。马从位置(x,y)处出发,把棋盘的每一点都走一次,且只走一次,找出所有路径。

棋盘坐标

对于棋盘上一点A(x,y)有八个拓展方向,即
A1(x+1,y+2)A2(x+2,y+1)A3(x+2,y-1)A4(x+1,y-2)
A5(x-1,y-2)A6(x-2,y-1)A7(x-2,y+1)A8(x-1,y+2)
用数组 fx[8] = { 1,2,2,1,-1,-2,-2,-1 } 和 fy[8] = { 2,1,-1,-2,-2,-1,1,2 } 来模拟马走日时下标的变化。

搜索过程从起始点(x,y)出发,按深度优先原则,从8个方向中寻找一个可以走的点,直到走过棋盘上所有点。

#include <stdio.h>
#include <string.h>
int matrix[10][9];//棋盘
int  journey = 1;//当前步数
int step_x[]={1,2,2,1,-1,-2,-2,-1},step_y[]={2,1,-1,-2,-2,-1,1,2};//横向和纵向移动的八个方向
 
//输出棋盘
void outMatrix(){
	int i,j;
	for (i=0;i<10;i++)
	{
		for (j=0;j<9;j++)
		{
			printf("%-2d ",matrix[i][j]);
		}
		printf("\n");
	}
}
 
//判断是否超出边界
bool outofbounds(int x,int y){
	return x < 0 || y < 0 || x >= 10 || y >= 9;
}

//递归遍历算法,八个方向
void gotoend(int x, int y ){
	if(journey>90) return;//避免无限递归
	int i;
	matrix[x][y]=journey++;			//棋盘记录当前为第几步
    //八个方向移动
	for (i = 0;i<8;i++) 
	{
		int next_x = x+step_x[i];//遍历横轴移动方向
		int next_y = y+step_y[i];//遍历纵轴移动方向
        //未访问过且未超出边界
		if(!outofbounds(next_x,next_y) && !matrix[next_x][next_y]){
			gotoend(next_x,next_y);
		}
	}
}

int main(){
	int start_x,start_y;
	int i;
	scanf("%d%d",&start_x,&start_y);
	for (i = 0;i<10;i++) {
        //分配初值
		memset(matrix[i],0,sizeof(matrix[0]));
	}
	gotoend(start_x,start_y);
	outMatrix();
	return 0;
}

设计算法输出1-10的全排列。

#include<cstdio>
using namespace std;

//每次排列后更新首元素
void swap(int arr[], int p, int q){
    int temp=arr[p];
    //排列元素全部后移
    for(int i=q; i>=p+1; i--){
        arr[i] = arr[i-1];
    }
    arr[p] = temp;
}
//排列完恢复首元素次序
void swapback(int arr[], int p, int q){
    int temp=arr[p];
    //排列元素全部后移
    for(int i=p; i<=q-1; i++){
        arr[i] = arr[i+1];
    }
    arr[q] = temp;
}

void printArr(int arr[], int n){
    for(int i=0; i<n; i++){
        printf("%d", arr[i]);
    }
    printf("\n");
}

void perm(int arr[], int p, int q){
    if(p==q){
        printArr(arr, q+1);//长度为n
    }else{
        for(int i=p; i<=q; i++){
            swap(arr, p, i);
            perm(arr, p+1, q);
            swap(arr, p, i);
        }
    }
}

int main(){
    int arr[] = {1, 2, 3};
    perm(arr, 0, 2);
}

求具有下列两个性质的最小自然数n

n的个位数是6;若将n的个位数移到其余各位数字之前,所得的新数是n的4倍。

#include<stdio.h>
#include<math.h>
int main()
{	 
	/*设 10x+6 是一个n位的正整数(n≥2) ,其中,x是一个n-1位的正整数,
	则由题意,6×10^(n-1)+x=4(10x+6),
	得 x=[6×10^(n-1)-24]/39=[2×10^(n-1)-8]/13
	检验,当 n =6时,x有最小正整数解,即最小自然数 n=153846*/
	int n;
	int x;	//x=[6×10^(n-1)-24]/39=[2×10^(n-1)-8]/13
	int x1; // 设x1=[6×10^(n-1)-24]/39=[2×10^(n-1)-8]  则x1=13*x  则当x1可整除13时    则所求数为10x+6 		
	for(n=2;;n++)//遍历自然数
	{
		x1=(int)(2*pow(10,n-1)-8);
		if(x1%13==0)
		{
			printf("这是个%d位数\n",n);
			x=x1/13; 
			printf("这个数是%d",10*x+6);
			break;
		}
	
	}
	return 0;
}

q+1);//长度为n
}else{
for(int i=p; i<=q; i++){
swap(arr, p, i);
perm(arr, p+1, q);
swap(arr, p, i);
}
}
}

int main(){
int arr[] = {1, 2, 3};
perm(arr, 0, 2);
}




## 求具有下列两个性质的最小自然数n

n的个位数是6;若将n的个位数移到其余各位数字之前,所得的新数是n的4倍。

```c
#include<stdio.h>
#include<math.h>
int main()
{	 
	/*设 10x+6 是一个n位的正整数(n≥2) ,其中,x是一个n-1位的正整数,
	则由题意,6×10^(n-1)+x=4(10x+6),
	得 x=[6×10^(n-1)-24]/39=[2×10^(n-1)-8]/13
	检验,当 n =6时,x有最小正整数解,即最小自然数 n=153846*/
	int n;
	int x;	//x=[6×10^(n-1)-24]/39=[2×10^(n-1)-8]/13
	int x1; // 设x1=[6×10^(n-1)-24]/39=[2×10^(n-1)-8]  则x1=13*x  则当x1可整除13时    则所求数为10x+6 		
	for(n=2;;n++)//遍历自然数
	{
		x1=(int)(2*pow(10,n-1)-8);
		if(x1%13==0)
		{
			printf("这是个%d位数\n",n);
			x=x1/13; 
			printf("这个数是%d",10*x+6);
			break;
		}
	
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值