回溯法是算法竞赛中解决01问题和排列问题的经典算法,主要有三种实现类型:
1)分支法;2)交换法;3)标志法
01问题
10个空用0或1填入有多少种填法(2的阶乘)
回溯算法启蒙版(分支法)
#include<iostream>
using namespace std;
//globle variable
int count=0;//the number of the final reuslts
int num=10;//length of reuslt
int result[10]={0};
//result output
void output(int a[]){
for(int i=0;i<10;i++){
cout<<a[i];
}
cout<<endl;
}
//t: recall counter
void backtrace(int t){
if(t>=num){//>=
count++;//result calculating
output(result);// for test
return;//the end of this branch
}
for(int i=0;i<=1;i++){
if(i==0) {
result[t]=0;
backtrace(t+1);
}
else if (i==1){
result[t]=1;
backtrace(t+1);
}
}
}
//main function
int main(){
Backtrace(0);
cout<<"result:"<<count;
return 0;
}
排列问题
10个空用10个数填入有多少种排列的方法(排列数)。
回溯算法极简版(交换法)
#include<iostream>
using namespace std;
//globle variable
int count=0;
int num=10;
int array[10]={0,1,2,3,4,5,6,7,8,9};
//function for swap
void swap(int &x, int &y){//&x,&y
int temp = x;
x = y;
y = temp;
}
//result output
void output(int a[]){
for(int i=0;i<10;i++){
cout<<a[i];
}
cout<<endl;
}
//t: backtrace counter
void backtrace(int t){
if(t>=num){//>=
count++;//result calculating
output(array);// for test
return;//the end of this branch
}
for(int i=t;i<10;i++){
swap(array[i],array[t]);
backtrace(t+1);//not t++
swap(array[i],array[t]);//swap for recover the initial position
}
}
//main function
int main(){
backtrace(0);
cout<<"result:"<<count;
return 0;
}
回溯算法进阶版(标志法)
#include<iostream>
using namespace std;
//globle variable
int count=0;//the number of the final reuslts
int num=10;//length of reuslt
int array[10]={0,1,2,3,4,5,6,7,8,9};//candicate numbers
bool flag[10]; //length of array
int result[10];//length of result
//flag initialization
void init(bool flag[],int result[]){
for(int i=0;i<10;i++){
flag[i]=false;
}
for(int i=0;i<num;i++){
result[i]=-1;
}
}
//result output
void output(int a[]){
for(int i=0;i<10;i++){
cout<<a[i];
}
cout<<endl;
}
//t: recall counter
void backtrace(int t){
if(t>=num){//>=
count++;//result calculating
//output(result);// for test
return;//the end of this branch
}
for(int i=0;i<10;i++){
if(flag[i]==false){
flag[i]=true;
result[t]=array[i];
backtrace(t+1);//not t++
flag[i]=false;
}
}
}
//main function
int main(){
init(flag,result);
backtrace(0);
cout<<"result:"<<count;
return 0;
}
回溯法实战:剪枝优化
01问题:《李白打酒》(蓝桥杯2014年C语言B组第3题)
标题:李白打酒
话说大诗人李白,一生好饮。幸好他从不开车。
一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:
无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。
请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数(包含题目给出的)。
注意:通过浏览器提交答案。答案是个整数。不要书写任何多余的内容。
#include<iostream>
using namespace std;
//globle variable
int count=0;//the number of the final reuslts
int countA=0;//the number of a
int countB=0;//the number of b
int countW=2;//the number of the wine
int num=15;//length of reuslt
char result[15]={};
void output(char a[]){//char
for(int i=0;i<15;i++){//15
cout<<a[i];
}
cout<<endl;
}
void backtrace(int t){
if(countA>5||countB>10||countW<0){//the branch cutting decision
return;//cutting
}
if(t>=num){
if(countA==5&&countB==10&&result[14]=='b'&&countW==0){//meet the conditions
count++;//then counting
output(result);//optional
}
return;
}
for(int i=0;i<=1;i++){
if(i==0) {
result[t]='a';//record result
countA++;//count the number of 'a'
countW*=2;//add to twice
backtrace(t+1);
countA--;//back to the init number of 'a' this place
countW/=2;//back to the init value this place
}
else if (i==1){
result[t]='b';//record result
countB++;//count the number of 'b'
countW-=1;//drink 1 dou
backtrace(t+1);
countB--;//back to the init number of 'b' this place
countW+=1;//back to init the value this place
}
}
}
//main function
int main(){
backtrace(0);
cout<<"result:"<<count;
return 0;
}
排列问题:三羊献瑞(2015年蓝桥杯C语言B组第3题)
三羊献瑞
观察下面的加法算式:
祥 瑞 生 辉
+ 三 羊 献 瑞
-------------------
三 羊 生 瑞 气(如果有对齐问题,可以参看【图1.jpg】)
其中,相同的汉字代表相同的数字,不同的汉字代表不同的数字。
请你填写“三羊献瑞”所代表的4位数字(答案唯一),不要填写任何多余内容。
#include<iostream>
using namespace std;
//globle variable
int count=0;
int num=8;
int array[10]={0,1,2,3,4,5,6,7,8,9};//"祥 瑞 生 辉 三 羊 献 气"
//function for swap
void swap(int &x, int &y){//&x,&y
int temp = x;
x = y;
y = temp;
}
//result output
void output(int a[]){
for(int i=0;i<8;i++){
cout<<a[i];
}
cout<<endl;
}
//judge whether the reuslt meet the condition
bool judge(int a[]){
int x=a[0]*1000+a[1]*100+a[2]*10+a[3];
int y=a[4]*1000+a[5]*100+a[6]*10+a[1];
int z=a[4]*10000+a[5]*1000+a[2]*100+a[1]*10+a[7];
if(x+y==z){
cout<<"三羊献瑞:"<<y<<endl;
return true;
}
else{
return false;
}
}
//t: backtrace counter
void backtrace(int t){
if(array[4]==0){//"三" can not be zero
return;//cutting
}
if(t>=num){
if(judge(array)){//final judge
count++;
output(array);
}
return;
}
for(int i=t;i<10;i++){
swap(array[i],array[t]);
backtrace(t+1);
swap(array[i],array[t]);
}
}
//main function
int main(){
backtrace(0);
cout<<"result:"<<count;
return 0;
}
#include<iostream>
using namespace std;
//globle variable
int count=0;//the number of the final reuslts
int num=8;//length of reusltint num=8;
int array[10]={0,1,2,3,4,5,6,7,8,9};//"祥 瑞 生 辉 三 羊 献 气"
bool flag[10]; //length of array
int result[8];//length of result
//flag initialization
void init(bool flag[],int result[]){
for(int i=0;i<10;i++){
flag[i]=false;
}
for(int i=0;i<num;i++){
result[i]=-1;//caution!
}
}
void output(int a[]){
for(int i=0;i<8;i++){
cout<<a[i];
}
cout<<endl;
}
//judge whether the reuslt meet the condition
bool judge(int a[]){
int x=a[0]*1000+a[1]*100+a[2]*10+a[3];
int y=a[4]*1000+a[5]*100+a[6]*10+a[1];
int z=a[4]*10000+a[5]*1000+a[2]*100+a[1]*10+a[7];
if(x+y==z){
cout<<"三羊献瑞:"<<y<<endl;
return true;
}
else{
return false;
}
}
void backtrace(int t){
if(result[4]==0){//"三" can not be zero
return;//cutting
}
if(t>=num){
if(judge(result)){//final judge
count++;
output(result);
}
return;
}
for(int i=0;i<10;i++){
if(flag[i]==false){
flag[i]=true;
result[t]=array[i];
backtrace(t+1);
flag[i]=false;
}
}
}
//main function
int main(){
init(flag,result);
backtrace(0);
cout<<"result:"<<count;
return 0;
}
后续将会完善题目用例……