现在有五个人,五本书,每个人喜欢的书籍不一样,每个人可能喜欢多本书籍,编写一个程序,输入为这五个人喜欢的书的情况,为一串以空格隔开的0和1,1代表喜欢,0代表不喜欢,每个人有五个数字,求满足所有人的方案,输出这些方案
输入如下 :
0 0 1 1 0
1 1 0 0 1
0 1 1 0 1
0 0 0 1 0
0 1 0 0 1
代码如下 :
import java.util.*;
public class HuiSuFivePeople {
static int[][] hobbies = new int[5][5];
static int[] result = {-1,-1,-1,-1,-1};
static int[] visit = {-1,-1,-1,-1,-1};
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
for(int i=0;i<5;i++){
for(int j=0;j<5;j++){
hobbies[i][j] = sc.nextInt();
}
}
tryNext(0);//从第一个人开始尝试分配书籍
}
public static void tryNext(int num){
for(int i=0;i<5;i++){ //遍历每一本书
if(hobbies[num][i] == 1&&visit[i]==-1){//假如这本书第num个人喜好并且没有被分配掉
result[num] = i+1; //分配第i本数给这个人,为了最后看的方便我增加了1,因为索引起始为0
visit[i] = 1; //标记这本书已经被分配了
if(num==4){ //如果最后一个人也分配到了书籍的话,就找到了一种解决方案,输出方案
System.out.println("找到了一种解决方案,如下");
for(int j=0;j<5;j++){
System.out.print(result[j]+" ");
}
System.out.println();
}else{
tryNext(num+1); //如果没有分配完成,就尝试分配下一本
}
visit[i]=-1; //第i本书之后的分配完成了或者后面有不能分配的,tryNext(num+1)执行后到这里,取消这次的分配,也就是回溯
result[num] = -1;
}
}
}
}
结果如下:
本题是较为简单的回溯法的题目,设计起来较为简单,其实本题中回溯就是利用递归在枚举中增加了遇到错误情况就直接跳过,尝试下一种循环,比如这题中,如果直接枚举就是枚举出所有的书籍分配方案,然后判断每种方案是否可以,可以就输出,虽然也可以做,但是很浪费,但是用了回溯,先从第一本书开始尝试,假如到了第i本无法分配了,那么第i本之后就不分配了,分配了也是错的,直接回溯到分配第i-1本,让第i-1本尝试分配下一本书,然后尝试分配第i本书,看看能否分配,假如分配到最后一本了,证明已经分配好了所有书,那么就输出结果就可以了,一直这样直到所有的书都已经分配过了所有可行的方案,就结束枚举。
这么做的还有一个好处,输出的方案,书籍的序号组成的数字串,所有方案的输出顺序是按照从字符串小的到字符串大的输出的。
下面,总结一下回溯法的基本套路
我做的回溯法都是用递归来做的,我的递归基本结构如下
public static void dfs(int n(走第n步) ){
for(遍历所有可以走的选择){
if(如果这一步可以走){
走起。。。。。。(一般在结果中走这一步,并且标记选择)
if(如果走到了最后一步){
得到了一种方案,记录下来
}else{ //如果没有走到最后一步
dfs(n+1(走下一步));
}
回溯,将刚才 走起 的操作回退
}
}
}
回溯的大部分问题都可以照着这个结构解决