【算法学习】回溯法


          回溯法英语backtracking)是暴力搜寻法中的一种。

       回溯法采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。回溯法通常最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况:

      (1) 找到一个可能存在的正确的答案。

      (2)在尝试了所有可能的分步方法后宣告该问题没有答案。

       在最坏的情况下,回溯法会导致一次复杂度指数时间的计算。(以上定义摘自wiki百科)

      

 

      但是只看定义对于了解和使用的效果是比较小的,我找了几道题目进行补充理解和练习。

#include<iostream>
using namespace std;

int ans = 0;
int m, n; 

void dfs(int x, int y)
{
	if(x == n && y == m)
	{
		ans++;
		return;
	}
	if(x + 1 <= n) dfs(x + 1, y);
	if(y + 1 <= m) dfs(x, y + 1);
}

int main()
{
	cin>>n>>m;
	dfs(1, 1);
	cout<<ans;
	return 0;
} 


 

 

 

#include<iostream>
using namespace std;

int n;
int a[50];

void dfs(int deep)
{
	if(deep > n)
	{
		for(int i = 1; i <= n; i++)
		{
			cout<<a[i]<<" ";
		}
		cout<<endl;
		return;
	}
	for(int i = 1; i <= n; i++)
	{
		a[deep] = i;
		dfs(deep + 1);
	}
	
}

int main()
{
	cin>>n;
	dfs(1);
	return 0;
} 

#include<iostream>
using namespace std;

int n;
int a[50], flag[50] = { 0 };

void dfs(int deep)
{
	if(deep > n)
	{
		for(int i = 1; i <= n; i++)
		{
			cout<<a[i]<<" ";
		}
		cout<<endl;
		return;
	}
	for(int i = 1; i <= n; i++)
	{
		if( !flag[i] )
		{
			flag[i] = 1; //标记表示 i 已经被用过 
			a[deep] = i;
			dfs(deep + 1);
			flag[i] = 0; //回溯 i 用过之后重新标记,表示可以再次使用 
		}
	}
	
}

int main()
{
	cin>>n;
	dfs(1);
	return 0;
} 


 

#include<iostream>
using namespace std;
//由上一个程序简单改进
//n里面选m个数  便在1~n中选数 递归到m层 
int n, m;
int a[50], flag[50] = { 0 };

void dfs(int deep)
{
	if(deep > m)
	{
		for(int i = 1; i <= m; i++)
		{
			cout<<a[i]<<" ";
		}
		cout<<endl;
		return;
	}
	for(int i = 1; i <= n; i++)
	{
		if( !flag[i] )
		{
			flag[i] = 1; //标记表示 i 已经被用过 
			a[deep] = i;
			dfs(deep + 1);
			flag[i] = 0; //回溯 i 用过之后重新标记,表示可以再次使用 
		}
	}
	
}

int main()
{
	cin>>n>>m;
	dfs(1);
	return 0;
} 



     但是这个代码并不难达到所示图片的样例,于是就有如下的代码。

#include<iostream>
using namespace std;
//由上一个程序简单改进
//n里面选m个数  便在1~n中选数 递归到m层 
int n, m;
int a[50], flag[50] = { 0 };

void dfs(int deep)
{
	int m_flag = 1; 
	if(deep > m)
	{
		for(int i = 1; i < m; i++)
		{
			if(a[i] > a[i+1])
			{
				m_flag = 0;
				break;
			}
		}//如果不是升序排列的话,就跳出循环,把m_flag赋值为0,不打印 
		if(m_flag)
		{
			for(int i = 1; i <= m; i++)
			{
				cout<<a[i]<<" ";
			}
			cout<<endl;
		}
		return;
	}
	for(int i = 1; i <= n; i++)
	{
		if( !flag[i] )
		{
			flag[i] = 1; //标记表示 i 已经被用过 
			a[deep] = i;
			dfs(deep + 1);
			flag[i] = 0; //回溯 i 用过之后重新标记,表示可以再次使用 
		}
	}
	
}

int main()
{
	cin>>n>>m;
	dfs(1);
	return 0;
} 

 

 

算法可以进一步优化

#include<iostream>
using namespace std;
//由上一个程序简单改进
//n里面选m个数  便在1~n中选数 递归到m层 
int n, m;
int a[50] = { 0 };

void dfs(int deep)
{
	int flag = 1;
	if(deep > m)
	{
		for(int i = 1; i <= m; i++)
		{
			cout<<a[i]<<" ";
		}
		cout<<endl;
		return;
	}
	for(int i = a[deep - 1] + 1; i <= n; i++)//递归之后直接从已选数字的后面找数 
	{
		a[deep] = i;
		dfs(deep + 1);
	}
	
}

int main()
{
	cin>>n>>m;
	dfs(1);
	return 0;
} 



 

题目标题39级台阶

小明刚刚看完电影《第39级台阶》,离开电影院的时候,他数了数礼堂前的台阶数,恰好是39!

站在台阶前,他突然又想着一个问题:

如果我每一步只能迈上1个或2个台阶。先迈左脚,然后左右交替,最后一步是迈右脚,也就是说一共要走偶数步。那么,上完39级台阶,有多少种不同的上法呢?

#include<iostream>
using namespace std ;

int sum = 0 ;

void f (int n, int state) ;

int main()
{
	f (39, 1) ;
	cout<<sum<<endl ;
	return 0 ;
}

void f (int n, int state)
{
	if (n == 0 && state == 1) sum++ ;
	else if (n > 0)
	{
		f (n-1, !state) ;
		f (n-2, !state) ;
	}
}


 

 

 

#include<iostream>
using namespace std;

int n, number = 0, a[17] = { 0 };

void dfs(int deep, int nextState, int sum)
{
	a[deep] = nextState; //赋值 
	
	if(deep >= 2 * n && sum == 0) //满足条件打印 
	{
		for(int i = 1; i <= 2 * n; i++)
		{
			cout<<a[i]<<" ";
		}
		cout<<endl;
		number++;
		return;
	}
	
	if(sum < 0 || deep > 2 * n) return; // 不满足条件递归返回 
	
	dfs(deep + 1, 0, sum + 1); //继续向下一层递归 ,假设下一个人是持有5元 
	dfs(deep + 1, 1, sum - 1); //继续向下一层递归,假设下一个人持有10元 
	
}

int main()
{
	int sum = 0;
	cin>>n;
	switch(n)//因为不知道应该怎么先打印数目,在打印具体方案,所以只能投机取巧了 
	{
		case 1:  cout<<1<<endl; break; 
		case 2:  cout<<2<<endl; break; 
		case 3:  cout<<5<<endl; break; 
		case 4:  cout<<14<<endl; break; 
		case 5:  cout<<42<<endl; break; 
		case 6:  cout<<132<<endl; break; 
		case 7:  cout<<429<<endl; break; 
		case 8:  cout<<1430<<endl; break; 
	}
	dfs(1, 0, sum + 1);
	return 0;
} 



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值