在之前见过的回溯法中,基本上都是在枚举数字的排列,直到没有解为止,没有要求其中的某一个解的情况
此题就是要在这一系列解中,求出第n个解
这就涉及到了如何返回的问题
先来看代码:
//这道题我的思路是首先按照字典序来构建字符串 //但是如何判断是否有重复的字串值得学习 //即:只需要判断当前加上的这个字符是否造成重复字串即可 #include<cstdio> using namespace std; const int maxn = 100; int seq,list; char ans[maxn]; int cnt = 0; bool valid(int cur) { //倒着判断是否合法,判断后2i位 for(int i = 1;2 * i <= cur + 1;i++ ) { int equal = 1; for(int j = cur,k = cur - i;j > cur - i;j--,k--) { if(ans[j] != ans[k]) equal = 0; } if(equal) { return false; } } return true; } void print_ans(int len)//len代表到下标的哪一位结束 { for(int i = 0;i <= len; i++) { if(i % 64 == 0 && i) printf("\n"); if(i % 4 == 0 && i % 64 != 0) printf(" "); printf("%c",ans[i]); } printf("\n"); } bool dfs(int cur) { if(cnt == seq) { print_ans(cur - 1); printf("%d\n",cur); return true; } else { for(int i = 0;i < list;i++) { ans[cur] = i + 'A'; if(valid(cur)) { cnt++; if(dfs(cur + 1)) return true; } } } return false; } int main() { #ifdef local freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); #endif while(scanf("%d%d",&seq,&list) == 2 && seq && list) { cnt = 0; dfs(0); } return 0; }
那么应该如何返回呢?
就是当前cur的所有的可能的尝试都试过之后,就应该返回上一个cur的下一个选择
而当找到了解之后,就应该直接返回在栈里面的所有的存在的函数
回到代码里面
如代码所示,如果找到了解,那么就返回true,并且这个true还会使所有的函数都返回true,直到全部返回
如果某一个cur的选择都选完了,需要返回上一层去,去选择上一层的下一个解,那么就应该返回false,让上一层开始下一个选择。
值得注意的是:千万不能让返回值缺省,因为bool中缺省返回值,默认的是true,那么在某一个cur的选择都选完了的时候,本应该返回false的时候,却因为缺省而返回了true,让上一层误以为已经找到了解。
第二点就是,如果找不到解,那么所有的函数都会返回false,即:都会选择到最后一刻。
上面还有注意的一点就是,避免不必要的判断,就是我之前在回溯法的分析当中提到的,如何高效率的判断当前的选择是否合法
同时,上面那个valid函数的编写以及输出形式的编写也需要好好品味一下。