回溯法浅谈

回溯法是初学者学习暴力法的第一个障碍,所谓回溯就是指当把问题分成若干步骤并递归求解时,如果当前步骤没有合法选择,则函数将返回上一级递归调用,这种现象称为回溯。正是因为这个原因,递归枚举算法常被称为回溯法,应用十分普遍。

八皇后问题

 1 int tot=0;
 2 int c[maxn];  //c[x]表示x行所在的列数
 3 void dfs(int n,int cur){
 4     if(cur==n) ++tot;
 5     for(int i=0;i<n;i++){
 6         int flag=0;
 7         c[cur]=i;
 8         for(int j=0;j<cur;j++)
 9             if(c[cur]==c[j]||(cur-c[cur]==j-c[j])||(cur+c[cur]==j+c[j])){
10                 flag=1; break;
11             }
12         if(!flag)
13             dfs(n,cur+1);
14     }
15 }
初始版

 一般地,若在回溯法中修改了辅助的全局变量,则一定要及时把它恢复原状(除非故意保留所做的修改)。

 1 int tot=0;
 2 int vis[3][maxn];   //分别表示所在列,主对角,副对角是否已经放了元素
 3 void dfs(int n,int cur){
 4     if(cur==n) ++tot;
 5     for(int i=0;i<n;i++){
 6         if(!vis[0][i]&&!vis[1][i+cur]&&!vis[2][cur-i+n]){
 7             vis[0][i]=vis[1][i+cur]=vis[2][cur-i+n]=1;
 8             dfs(n,cur+1);
 9             vis[0][i]=vis[1][i+cur]=vis[2][cur-i+n]=0;
10         }
11     }
12 }  
优化版

 特别地,若函数有多个出口,则在每个出口处恢复被修改的值。

 

UVA524

分析:用vis标记每一位是否访问过了,记得最后把修改过的vis数组还原

 1 #include "iostream"
 2 #include "cstdio"
 3 #include "cstring"
 4 using namespace std;
 5 const int maxn=25;
 6 int a[maxn];
 7 int vis[maxn];
 8 int n;
 9 bool prime(int x){
10     if(x==2)
11         return true;
12     for(int i=2;i*i<=x;i++)
13         if(x%i==0)
14             return false;
15     return true;
16 }
17 void dfs(int n,int cur){
18     if(cur==n&&prime(a[0]+a[n-1])){
19         for(int i=0;i<n-1;i++)
20             printf("%d ",a[i]);
21         printf("%d\n",a[n-1]);
22     }
23     else{
24         for(int i=2;i<=n;i++){
25             if(!vis[i]&&prime(a[cur-1]+i)){
26                 a[cur]=i;
27                 vis[i]=1;
28                 dfs(n,cur+1);
29                 vis[i]=0;
30             }
31         }
32     }
33 }
34 int main()
35 {
36     int cnt=0;
37     while(cin>>n)
38     {
39         if(cnt++)
40             printf("\n");
41         printf("Case %d:\n",cnt);
42         memset(a,0,sizeof(a));
43         memset(vis,0,sizeof(vis));
44         a[0]=1;
45         vis[1]=1;
46         dfs(n,1);
47     }
48     return 0;
49 }
View Code

 

UVA129

分析:问题的关键在于如何判断当前字符串是否已经存在连续子串。跟八皇后类似,我们可以只去判断当前串的后缀,而不需要去判断所有子串。看后一半是否等于前一半,这样可以省去很多不必要的判断。

 1 #include "iostream"
 2 #include "cstdio"
 3 #include "cstring"
 4 using namespace std;
 5 const int maxn=110;
 6 int s[maxn];
 7 int n,l;
 8 int cnt;
 9 bool dfs(int cur){
10     if(cnt++==n){
11         for(int i=0;i<cur;i++){
12             if(i%64==0&&i)
13                 printf("\n");
14             else if(i%4==0&&i)
15                 printf(" ");
16             printf("%c",'A'+s[i]);
17         }
18         printf("\n");
19         printf("%d\n",cur);
20         return false;
21     }
22     for(int i=0;i<l;i++){
23         int ok=0;
24         s[cur]=i;
25         for(int j=1;2*j<=cur+1;j++){  //枚举2*j长度的后缀
26             int flag=0;
27             for(int k=0;k<j;k++){    //判断是否出现重复子串
28                 if(s[cur-k]!=s[cur-j-k]){
29                     flag=1; break;
30                 }
31             }
32             if(!flag){   //出现重复子串,不满足
33                 ok=1; break;  
34             }
35         }
36         if(!ok){
37             if(!dfs(cur+1))  return false;
38         }
39     }
40     return true;
41 }
42 int main()
43 {
44     while(cin>>n>>l)
45     {
46         if(!n&&!l)  break;
47         memset(s,0,sizeof(s));
48         cnt=0;
49         bool flag=dfs(0);
50     }
51 }
View Code

 

posted @ 2017-04-12 01:36  wolf940509  阅读(474)  评论(0编辑  收藏  举报