zoukankan      html  css  js  c++  java
  • 回溯法浅谈

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

    八皇后问题

     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
    ",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("
    ");
    41         printf("Case %d:
    ",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("
    ");
    14             else if(i%4==0&&i)
    15                 printf(" ");
    16             printf("%c",'A'+s[i]);
    17         }
    18         printf("
    ");
    19         printf("%d
    ",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
  • 相关阅读:
    HIbernate小结
    玩耍Hibernate之缓存
    Java学习之IO流总结
    java基础学习之对象转型
    玩耍Hibernate系列(一)补充--基础知识
    玩耍Hibernate系列(二)--基础知识
    玩耍Hibernate系列(一)--基础知识
    Struts2拦截器的使用 (详解)
    struts1 和 struts2中Action什么时候实例化
    Java基础学习总结--Java对象的序列化和反序列化
  • 原文地址:https://www.cnblogs.com/wolf940509/p/6693963.html
Copyright © 2011-2022 走看看