Backtracking

来源:互联网 发布:人人商城v2 数据库 编辑:程序博客网 时间:2024/05/24 05:31

1. Introduction
Backtracking is a method of exhaustive search. When a node reaches certain termination condition that means this path will not have solutions, the program will go back to the previous node and try another path.

2. Example
1. Generate all the strings of n bits.

int A[4];int C=0;void Print(int a[],int n){    for(int i=0;i<n;i++){        cout<<a[i];    }    cout<<endl;    C++;}void Binary(int n){    if(n<1){        Print(A,4);    }    else{        A[n-1]=0;        Binary(n-1);        A[n-1]=1;        Binary(n-1);    }}

*Here is an example when n=4.

2. Generate all the strings of length n chosen from 0,1,,,,k-1.

int A[5];int C=0;void Print(int a[],int n){    for(int i=0;i<n;i++){        cout<<a[i];    }    cout<<endl;    C++;}void kstring(int n,int k){    if(n<1){        Print(A,5);    }    else{        for(int i=0;i<k;i++){            A[n-1]=i;            kstring(n-1,k);        }    }}

*Here is an example when n=5.

3. Combination

int A[5]={1,2,3,4,5};int R[3];int C=0;void Print(int a[],int n){    for(int i=0;i<n;i++){        cout<<a[i];    }    cout<<endl;    C++;}void Combination(int start,int n,int a[],int r[]){    if(n<1){        Print(r,3);    }    else{        for(int i=start;i<=5-n;i++){            r[3-n]=a[i];            Combination(i+1,n-1,a,r);        }    }}

*Here is an example of choose 3 from 5.

4. Full permutation

Method1:

void Swap(int* a,int* b){    int t=*a;    *a=*b;    *b=t;}void Permutation(int t[],int n,int start,int end){    if(n<1){        Print(t,3);    }    else{        for(int i=start;i<=end;i++){            Swap(&t[start],&t[i]);            Permutation(t,n-1, start+1, end);            Swap(&t[start],&t[i]);        }    }}

*Here is an example of a list with 3 elements.

Method2:

const int n=5;int numbers[5]={1,2,3,4,5};int R[5];int indicate[5]={0};int C=0;void Print(int A[],int m){        for(int i=0;i<m;i++){            cout<<A[i]<<' ';        }        cout<<endl;    C++;}void Permutation(int cur){    if(cur>=n) Print(R, n);    else{        for(int i=0;i<n;i++){            if(!indicate[i]){               R[cur]=numbers[i];                indicate[i]=1;                Permutation(cur+1);                indicate[i]=0;            }        }    }}

Note that here it is necessary to have a R list to store the current solution. If we use the list numbers to store solutions, the problem is that the index of numbers will change and hence the indicate list will record the wrong situation.

5. Permutation

void Print(int a[],int n){    for(int i=0;i<n;i++){        cout<<a[i];    }    cout<<endl;    C++;}void Swap(int* a,int* b){    int t=*a;    *a=*b;    *b=t;}void Permutation(int t[],int n,int start,int end){    if(n<1){        Print(t,3);    }    else{        for(int i=start;i<=end;i++){            Swap(&t[start],&t[i]);            Permutation(t,n-1, start+1, end);            Swap(&t[start],&t[i]);        }    }}void Combination(int n,int start, int a[],int r[]){    if(n<1){        Permutation(r,3, 0, 2);    }    else{        for(int i=start;i<=5-n;i++){            r[3-n]=a[i];            Combination(n-1, i+1, a, r);        }    }}

*Here is an example of choose 3 from 5, the basic idea is to find all combinations first and then do full permutation to each combination.

3. Problems and solutions
1. N-Queen problem

void search(int curline){    if(curline==n) {        tot++;        for(int i=0;i<n;i++){            cout<<C[i]<<' ';        }        cout<<endl;    }    else{        for(int i=0;i<n;i++){            int ok=1;            C[curline]=i;            for(int j=0;j<curline;j++){                if(C[curline]==C[j]||curline-C[curline]==j-C[j]||curline+C[curline]==j+C[j]){                        ok=0;                        break;                    }                }            if(ok) search(curline+1);            }        }    }

Here we use a one-dimension list to indicate the position of chess and we use mathematical relation between line and column number as if-conditions to determine whether the solution is acceptable.

We can also do this in the following method:

void search(int cur){    if(cur==n){        tot++;        for(int i=0;i<n;i++){            cout<<C[i]<<' ';        }        cout<<endl;    }    else{        for(int i=0;i<n;i++){            if(!vis[0][i]&&!vis[1][cur+i]&&!vis[2][i-cur+n]){                C[cur]=i;                vis[0][i]=vis[1][cur+i]=vis[2][i-cur+n]=1;                search(cur+1);                vis[0][i]=vis[1][cur+i]=vis[2][i-cur+n]=0;            }        }    }}

Here we use a two-dimensional list vis to indicate the column and diagonals that are already occupied by previous chess. Note that after recursively call function search for the next line, we need to change back the value of that position in vis for it to work on the next column of the current line.

2. Prime Ring Problem (UVa 524)

Method 1: (Enumeration, brute force)

#include <iostream>#include <cmath>using namespace std;int const n=6;int inputs[n]={1};int isp[2*n+1];int is_prime(int a){    int t=sqrt(a);    int ok=1;    for(int i=2;i<=t;i++){        if(a%i==0){            ok=0;            break;        }    }    if(ok) return 1;    else return 0;}void Swap(int*a, int*b){    int t=*a;    *a=*b;    *b=t;}void Print(int A[],int m){    for(int i=0;i<m;i++){        cout<<A[i]<<' ';    }    cout<<endl;}void Decide(){    int ok=1;    for(int i=0;i<n;i++){        if(!isp[inputs[i]+inputs[(i+1)%n]]){            ok=0;            break;        }    }    if(ok) Print(inputs,n);}void Permutation(int start){    if(start>=n){        Decide();    }    else{        for(int i=start;i<n;i++){            Swap(&inputs[i],&inputs[start]);            Permutation(start+1);            Swap(&inputs[i],&inputs[start]);        }    }}int main(){    for(int i=0;i<n;i++){        inputs[i]=i+1;    }    for(int i=2;i<=2*n;i++){        isp[i]=is_prime(i);    }    Permutation(1);}

This method will exceed time limit when n gets bigger.

Method 2: (Backtracking)

#include <iostream>#include <cmath>using namespace std;const int n=6;int A[n]={1};int vis[n]={0};int is_prime(int a){        int t=sqrt(a);        int ok=1;        for(int i=2;i<=t;i++){            if(a%i==0){                ok=0;                break;            }        }        if(ok) return 1;        else return 0;}void Print(int A[],int m){            for(int i=0;i<m;i++){                cout<<A[i]<<' ';            }            cout<<endl;}void dfs(int cur){    if(cur==n&&is_prime(A[0]+A[n-1])){        Print(A, n);    }    else{        for(int i=2;i<=n;i++){            if(!vis[i]&&(is_prime(i+A[cur-1]))){                A[cur]=i;                vis[i]=1;                dfs(cur+1);                vis[i]=0;            }        }    }}int main(){    dfs(1);}

3. Krypton Factor (UVa 129)

#include <iostream>#include <cstdio>using namespace std;const int L=3;const int n=30;int S[200];int cnt=0;int dfs(int cur){    if(cnt==n){        for(int i=0;i<cur;i++){            printf("%c",'A'+S[i]);        }        printf("\n");        return 0;    }    for(int i=0;i<L;i++){        S[cur]=i;        int ok=1;        for(int j=1;j<=(cur+1)/2;j++){            int equal=1;            for(int k=0;k<j;k++){                if(S[cur-k]!=S[cur-k-j]){                    equal=0;                    break;                }            }            if(equal){                ok=0;                break;            }        }        if(ok){            cnt++;            if(!dfs(cur+1)){                return 0;            }        }    }    return 1;}int main(){    dfs(0);}
0 0