算法竞赛之回溯法

回溯法是算法竞赛中解决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;
}


后续将会完善题目用例……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值