以2019蓝桥杯C/C++B组的两个题目为例,来进行说明回溯法的递归和迭代版本
作为篮球队教练,你需要从以下名单中选出 1 号位至 5 号位各一名球员,组成球队的首发阵容。每位球员担任 1 号位至 5 号位时的评分如下表所示。请你计算首发阵容 1号位至 5 号位的评分之和最大可能是多少?
此处的递归和迭代都是用于遍历全部解空间寻找最优解,所以思路是差不多的,每个人每个位置都尝试一下。但是这个尝试的顺序对于代码的复杂度有很大的关系。我们应该固定站位信息,每个位置的球员做循环,其实五层嵌套循环也能解决这个问题,但是那样代码就太不优雅了。
这里归纳一下,回溯法递归与迭代方式之间转换的通用方法:
- 由于都是在二维向量中查找最优组合,所以我们以一个所有元素必须取值的轴作为外层的轴对象,在这道题目中,就是以位置作为这条轴,因为这个变量可以依次取值,到第5号位的时候,就可以计算结果了,直接利用这个条件能够减少很多判断条件。
- 位置和球员之间的关系可以用一个一维数组表示,a[位置] = 球员,这样在迭代回溯的过程中可以直接通过x[位置–]返回上一个位置,x[位置]++来访问下一个球员,而且回溯的过程中,位置和球员的信息被关联到一起,使得可以进行下一次搜索。
- 要注意搜索的条件,如这道题使用一维数组来记录球员是否已经被选择了,要注意回溯时相关条件的改变,比如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;
}