zoukankan      html  css  js  c++  java
  • 回溯算法————n皇后、素数串

    回溯就是算法是搜索算法中一种控制策略,是一个逐个试探的过程。在试探的过程中,如果遇到错误的选择,就会回到上一步继续选择下一种走法,一步一步的进行直到找到解或者证明无解为止。

    如下是一个经典回溯问题n皇后的解答树:

    image

    下面就从n皇后说起:


    【问题描述】

    在n×n的国际象棋盘上,放置n个皇后,使任何一个皇后都不能吃掉另一个,需满足的条件是:同一行、同一列、同一对角线上只能有一个皇后。求所有满足要求的放置方案。4皇后的放置方案如下:

    【输入】一个正整数n。

    【输出】每行代表一种放置方案:第i行的第一个数是i,表示第i种方案,后面一个冒号,然后是用空格隔开的n个数,其中第i个数x[i]表示第i行上的皇后放在第x[i]列;最后一行:一个整数,表示方案总数。

    【样例输入】

    4

    【样例输出】

    1:2 4 1 3

    2:3 1 4 2

    2

    image

    这个题目简直是太经典了,一提到回溯第一个想到的就是它。这个题非常的通俗易懂,就是在一个n*n的棋盘上满足条件地放置n个皇后,每种组合搜一遍就好了。在搜的过程中判断下一步的某种方向是否可以走,如果可以的话就进入下一层,如果不符合要求就搜下一个方向,搜完这一层的所有方向以后就回到上一层,继续搜上一层的下一个方向。

    不知道大家看懂了没有,如果没看懂,看代码就懂了。。

    上代码:

    #include<iostream>
    #include<cstdio>
    
    using namespace std;
    
    int n;
    int sum=0;//解法存放个数
    int a[100000];//存放皇后位置数据
    bool b[1000000]={0},c[1000000]={0},d[10000000]={0};//b存储y方向,c、d存储对角线
    
    void print()
    {
    	sum++;
    	cout<<sum<<':';
    	for(int i=1;i<=n;i++)
    	{
    		cout<<a[i]<<' ';
    	}
    	cout<<endl;
    }
    
    int search(int x)
    {
    	for(int j=1;j<=n;j++)//遍历本层中每种方向
    	{
    		if((!b[j])&&(!c[x+j])&&(!d[x-j+n-1]))//如果满足条件
    		{
    			a[x]=j;//存入皇后数据
    			b[j]=1;//占领y轴
    			c[x+j]=1;//占领对角线
    			d[x-j+n-1]=1;//占领对角线
    			if(x==n) 
    			{
    				print();//如果有完整解,输出
    			}
    			else
    			{
    				search(x+1);//如果没有继续找下一行
    			} 
    			b[j]=0;//找完之后取消占领
    			c[x+j]=0;
    			d[x-j+n-1]=0;
    		}
    	}
    }
    
    int main()
    {
    	cin>>n;
    	search(1);//从第一个开始找
    	cout<<sum;
    } 
    

      把思想带到代码里应该就看懂了吧。

    回溯就是这样一层一层的找,找完本层返回上一层继续找,直到找到正确解为止。

    继续再看一道题:

    3、素数链

    设计程序将1。。。n排成一排,使任意两个相邻的数的和为素数。第1个和最后一个的和也为素数.输出一种方案即可。

    输入:

            n (n<=100)

    输出:

             1到n的一个序列,中间用一个空格隔开。第一个数为1。

            如果无解输出-1。

    样例输入:

    10

    样例输出:

    1  2  3  4  7  6  5  8  9  10

    这道题和刚才那道n皇后的思路是一样的,遍历所有可能,在遍历的过程中判断,如果可以就继续搜下一层。

    代码如下:

    #include<cstdio>
    #include<iostream>
    
    const int maxx=1000;
    
    using namespace std;
    
    int n;
    int numguo[maxx]={0};
    bool guo[maxx]={0},pguo=0;
    int c=0;
    
    int sushu(int x)//验证素数的函数
    {
    //关于判断素数的题很早就做过,这里我就不解释了
    	int i=2;
    	while(i<x)
    	{
    		if(x%i==0)
    		return 0;//如果不是直接跳出返回0
    		i++;
    	}
    	return 1;//如果是的话返回1
    }
    
    int panduan()
    {
    	int t;
    	for(int i=1;i<n;i++)//判断是否达到要求
    	{
    		if(!sushu(numguo[i]+numguo[i+1]))//这里我直接放到函数里去验证
    		{
    			return 0;//如果不是素数就直接跳出返回0
    		}
    	}
    	if(!sushu(numguo[n]+numguo[1])) return 0;//注意别忘了最后一个和第一个数字的和是否为素数
    	return 1;//正确的话返回1
    }
    
    int print()
    {
    	c++;//可能方法+1
    	for(int i=1;i<=n;i++){
    		cout<<numguo[i]<<' ';
    	}
    	cout<<endl;
    }
    
    int search(int x)
    {
    	for(int i=1;i<=n;i++)//搜索本层中每种可能
    	{
    		if(!guo[i])
    		{
    			guo[i]=1;//占领
    			numguo[x]=i;//标记
    			if(x==n)//如果达到数量要求
    			{
    				
    				if(panduan())//如果符合要求
    				{
    					if(!pguo)//如果没输出过
    					{
    						pguo=1;//输出过
    						print();//输出
    						return 0;//搜到就直接跳出
    					}
    				}
    			}
    			else search(x+1);
    			guo[i]=0;
    			numguo[x]=0;
    		}
    	}
    }
    
    int main()
    {
    	cin>>n;
    	search(1);//从第一个开始找
    	if(!c)	cout<<"-1";//如果没可能就输出“-1”
    	return 0;
    }
    

      这些代码中我用了很多的函数,这样节约了代码长度,也更加方便(个人觉得)。

    回溯就是这个中心思想,一步步往下搜,一层层的搜,直到搜完或找到结果为止。

    其实上面两道题都是很水的基础,再往后还有回溯遍历图、树等等等等。。

    ending。。。

  • 相关阅读:
    源码篇——安卓启动流程
    Android源代码结构分析
    使用Contacts Contract Content Provider操作通讯录最佳实践
    Linux输入子系统 : 按键驱动
    运算类实现 及 GNU Makefile基本结构
    输入系统:epoll & inotify
    FFmpeg:初步编译使用[Android]
    Linux学习 :SPI通讯协议
    Linux学习 : 自己写bootloader
    Linux学习 :移植U-boot_2012.04.01到JZ2440开发板
  • 原文地址:https://www.cnblogs.com/zhangone/p/5056106.html
Copyright © 2011-2022 走看看