回溯算法的四道经典题目,组合,子集,图着色和n皇后

认识回溯算法

回溯算法说白了就是穷举法。 不过回溯算法使用剪枝函数,剪去一些不可能到达 最终状态(即答案状态)的 节点 ,从而减少状态空间树节点的生成。 回溯法 是一个既带有系统性又带有跳跃性的的 搜索算法 。回溯算法通常拿来解组合、排列和子集等问题。
模板:

        def backtracking():
            if ( ):
    	        ...
                return
            for i in range():
				...
                backtracking()
                path.pop()

组合

题目:
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
示例 1:

输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
示例 2:

输入:n = 1, k = 1
输出:[[1]]
在这里插入图片描述
有人疑惑为什么到选2的时候只有[3,4]没有1,这是因为如果有1就和前面的[1,2]重复了,后面也是同理
,只要选这个数后面的数字就可以了。
令path=[],观察发现遍历深度为k次,遍历宽度为n-k+len(path[])+1次,当len(path)=k时返回结果
回溯的关键点就是找到遍历深度、宽度和结束条件。到了叶子节点返回的时候要pop(),例如path=[1,2],要path.pop()才可以让path=[1],然后再添加得到[1,3]。
代码:

class Solution(object):
    def combine(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: List[List[int]]
        """
        path=[]
        res=[]
        def backtracking(startIndex,n,k):
            if len(path)==k:#当len(path)==k时添加
                res.append(path[:])
                return
            for i in range(startIndex,n-k+2+len(path)):#这里为是n-k+2+len(path),因为要遍历n-k+len(path[])+1 次,左闭右开
                path.append(i)
                backtracking(i+1,n,k)#回溯
                path.pop()#删除
        backtracking(1,n,k)
        return res

子集

题目:
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:

输入:nums = [0]
输出:[[],[0]]
在这里插入图片描述
观察发现我们想要的结果就是要画红线的地方,当到num的下标为len(nums)时深度结束,下标为len(nums)时结束,和上一题不同的地方就是不是在叶子节点处收集结果。
代码:

class Solution(object):
    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result=[]
        path=[]
        def backtracking(nums,startIndex):
            result.append(path[:])
            for i in range(startIndex,len(nums)):
                path.append(nums[i])
                backtracking(nums,i+1)
                path.pop()
        backtracking(nums,0)
        return result

图着色

题目:
在一个有n个节点的图上,有m种颜色,要求相邻节点颜色不可以相同,打印出有多少种涂法。
例子:
4个节点,3种颜色
在这里插入图片描述
答案:
1213
1312
2123
1321
3132
3231

按照以上两题的思路我们可以知道深度为n,宽度为m;也就是每节点有四种颜色要遍历,因为相邻节点的颜色不可以相同,所以要有一个剪枝函数来判断是否满足条件,当走到最后一个节点时返回。这题目是我的作业所以就用c++来写了。

#include<iostream>
using namespace std;
class GraphColoring{
	public:
		GraphColoring(int x,int y)
		{	vertex=x;
			numberColors=y;
			Graph=new int*[x];//初始化图 
			for(int i=0;i<x;i++)
			{
				Graph[i]=new int[x];
			}
			for(int i=0;i<x;i++)
			{
                for(int j=0;j<x;j++)
				{
                    Graph[i][j]=0;
                }
            }
			colour=new int[x];//初始化颜色搭配数组 
			for(int i=0;i<x;i++)
			{
				colour[i]=0;
			} 	
		}
		void InsertEdge();//插入图的边 
		bool isvalidity(int Index,int currentColor);//判断在该点,这种颜色符不符合条件 
		void backtracking(int starIndex);
	private:
		int vertex,numberColors,*colour,**Graph;//vertex为图的节点个数,numberColors为颜色总数,colour为颜色搭配的数组,Graph是图 
};
void GraphColoring::InsertEdge()
{
	int i,j;
	cout<<"输入连接的两个顶点:";
	cin>>i>>j;
	Graph[i][j]=1;//无向图,直接一次赋两次值 
	Graph[j][i]=1;	
}
bool GraphColoring::isvalidity(int Index,int currentColor)
{
	for(int i=0;i<vertex;i++)
	{
		if(Graph[Index][i]==1 && colour[i]==currentColor)//遍历与该点连接的节点,看颜色是否相同 
		{
			return false;
		}
	}
	return true;
}
void GraphColoring::backtracking(int Index)
{
	if (Index==vertex)//当走到最后一个节点时,说明这种涂法符合条件 
	{

		for(int i=0;i<vertex;i++)
		{
			cout<<colour[i];//输出颜色搭配 
		}
		cout<<endl;
		return;
    }
	for(int currentColor=1;currentColor<numberColors+1;currentColor++)
	{
		if (not(isvalidity(Index,currentColor)))
		{
			continue;
		}
		colour[Index]=currentColor;
		backtracking(Index+1);
		colour[Index]=0;//删除颜色 
	}
		
}
int main(){
	int v,c,e;
	cout<<"分别输入顶点的个数,颜色的总数,边的条数:";
	cin>>v>>c>>e;
	GraphColoring G(v,c);
	for(int i=0;i<e;i++)
	{
		G.InsertEdge();	
	}
	G.backtracking(0);	
}

n皇后

题目:
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

示例 1:
带尺寸的图片: Alt

输入:n = 4
输出:[[".Q…","…Q",“Q…”,"…Q."],["…Q.",“Q…”,"…Q",".Q…"]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:

输入:n = 1
输出:[[“Q”]]

我们可以把数组给拆成一行一行,这样我们就可以知道深度为n,每个皇后的有四种摆法,当row等于n时说明这种摆法符合条件,然后通过剪枝函数就可以排除掉那些不符合条件的摆法。
代码:

class Solution(object):
    def solveNQueens(self, n):
        """
        :type n: int
        :rtype: List[List[str]]
        """
        res=[]
        board=[['.']*n for i in range(n)]
        def isvalidity(row,column):
            for i in range(len(board)): #判断同列是否满足条件
                if board[i][column] == 'Q':
                    return False
            i = row -1
            j = column -1
            while i>=0 and j>=0:#判断135度是否满足条件
                if board[i][j] == 'Q':
                    return False
                i -= 1
                j -= 1
            i = row - 1
            j = column + 1
            while i>=0 and j < len(board):#判断45度是否满足条件
                if board[i][j] == 'Q':
                    return False
                i -= 1
                j += 1
            return True
        def backtracking(row):
            if row==n:
                temp=[]
                for i in board:
                    str=''.join(i)
                    temp.append(str)
                res.append(temp)
                return 
            for column in range(n):
                if not isvalidity(row,column):
                    continue
                board[row][column]='Q'
                backtracking(row+1)
                board[row][column]='.'#恢复原来的模样
        backtracking(0)
        return res
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会按照您的要求分别给出n皇后问题子集和数问题的用回溯法解决的算法思路分析。 1. n皇后问题 n皇后问题是一个经典的八皇后问题的扩展,它的目标是在n × n的棋盘上放置n个皇后,使得每个皇后都不会互相攻击,即每个皇后都不在同一行、同一列和同一条对角线上。 回溯法是解决n皇后问题的常用方法。具体算法步骤如下: (1)定义一个n维数组board,用于表示棋盘,其中board[i]表示第i行皇后的列数。 (2)从第一行开始,依次尝试在每一列放置皇后,判断当前位置是否合法,若合法,则继续放置下一行的皇后,若不合法,则回溯到上一行,换一个位置继续尝试。 (3)当放置完最后一行的皇后时,即找到了一组解,输出结果。 (4)重复步骤2和3,直到找到所有解为止。 2. 子集和数问题 给定一组正整数和一个目标数,在这组数中找出若干个数,使得它们的和等于目标数。要求找出所有满足条件的数的组合回溯法同样也是解决子集和数问题的常用方法。具体算法步骤如下: (1)定义一个数组subset,用于记录当前的组合。 (2)依次尝试将每个数加入subset中,若当前和等于目标数,则输出结果,回溯到上一步;若当前和小于目标数,则继续尝试加入剩余的数;若当前和大于目标数,则回溯到上一步,换一个数继续尝试。 (3)重复步骤2,直到找到所有满足条件的组合为止。 以上就是n皇后问题子集和数问题回溯法解决的算法思路分析。希望能对您有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值