回溯法的递归与迭代版本

  以2019蓝桥杯C/C++B组的两个题目为例,来进行说明回溯法的递归和迭代版本

作为篮球队教练,你需要从以下名单中选出 1 号位至 5 号位各一名球员,组成球队的首发阵容。每位球员担任 1 号位至 5 号位时的评分如下表所示。请你计算首发阵容 1号位至 5 号位的评分之和最大可能是多少?
在这里插入图片描述
  此处的递归和迭代都是用于遍历全部解空间寻找最优解,所以思路是差不多的,每个人每个位置都尝试一下。但是这个尝试的顺序对于代码的复杂度有很大的关系。我们应该固定站位信息,每个位置的球员做循环,其实五层嵌套循环也能解决这个问题,但是那样代码就太不优雅了。

这里归纳一下,回溯法递归与迭代方式之间转换的通用方法:

  1. 由于都是在二维向量中查找最优组合,所以我们以一个所有元素必须取值的轴作为外层的轴对象,在这道题目中,就是以位置作为这条轴,因为这个变量可以依次取值,到第5号位的时候,就可以计算结果了,直接利用这个条件能够减少很多判断条件。
  2. 位置和球员之间的关系可以用一个一维数组表示,a[位置] = 球员,这样在迭代回溯的过程中可以直接通过x[位置–]返回上一个位置,x[位置]++来访问下一个球员,而且回溯的过程中,位置和球员的信息被关联到一起,使得可以进行下一次搜索。
  3. 要注意搜索的条件,如这道题使用一维数组来记录球员是否已经被选择了,要注意回溯时相关条件的改变,比如sum之前增加过,回溯之后要把sum-前一个加上去的值。

 具体的改写方式,见下列代码。理论上都是可以改写的。
C++代码

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

int a[20][6] = {{1,97,90,0,0,0},
                {2,92,85,96,0,0},
                {3,0,0,0,0,93},
                {4,0,0,0,80,86},
                {5,89,83,97,0,0},
                {6,82,86,0,0,0},
                {7,0,0,0,87,90},
                {8,0,97,96,0,0},
                {9,0,0,89,0,0},
                {10,95,99,0,0,0},
                {11,0,0,96,97,0},
                {12,0,0,0,93,98},
                {13,94,91,0,0,0},
                {14,0,83,87,0,0},
                {15,0,0,98,97,98},
                {16,0,0,0,93,86},
                {17,98,83,99,98,81},
                {18,93,87,92,96,98},
                {19,0,0,0,89,92},
                {20,0,99,96,95,81}};
int vis[21]; //20个队员是否被访问过,放21是怕遍历的时候越界
int max_score = 0;

//index表明了递归的深度,也表明是哪个位置,sum是当前情况的总分
void DFS(int index, int sum)
{
	if(index == 6) //已经选了5个人,这是后可以记分了
	{
		max_score = max(max_score, sum);
		return;
	}
	for(int i=0; i<20; i++) //对于每一个队员,每一个位置都尝试一下
	{
		if(vis[i] == 0) //只有这个队员没有被安排的时候才访问它
		{
			vis[i] = 1;
			DFS(index+1, sum+a[i][index]); //此时将该队员的该位置的得分和已选择了的做比较
			vis[i] = 0; //重新尝试下一种情况
		}
	}
}


void DFS_Iterator()
{
	int k=1; //第一个站位
	int x[6] = {0}; //相应站位对应队员
	int sum = 0;
	x[1] = -1;
	
	while(k > 0)  //控制站位数
	{
		x[k] += 1;
		while((x[k]<20) && vis[x[k]] == 1) //该队员被征用了就设为1
			x[k]++; //队员号提升一个
		sum += a[x[k]][k];
		vis[x[k]] = 1; //该队员占据这个位置
		if(x[k]<20)  //队员号不能越界
		{
			if(k == 5)
			{
				max_score = max(max_score,sum);
				sum -= a[x[k]][k];  //此处也需要减去刚刚的结果
				vis[x[k]] = 0;
			}
			else
			{
				k++; //向下一行扫描
				x[k] = -1; //下一个队员又从位置0开始扫描
			}
		}else
		{
			k--;  //回到上一个位置的情况,由于x[k]里面已经保存了刚才队员的信息
			sum -= a[x[k]][k];  // 这里减去的是上一行的信息,因为上一行需要向前移动
			vis[x[k]] = 0;
		}
			
	}
}

int main()
{
    //DFS(1,0);
	DFS_Iterator();
    cout<<max_score;
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值