深度优先搜索DFS与回溯

导入:数独问题  深入浅出程序设计竞赛187页

学生基础:必须在熟练掌握递归和暴力枚举的基础上

需要讲解:函数栈空间

P1706 全排列问题

#include<iostream>
using namespace std;
int n;
int v[10];//标记i有没被选中
int a[10];//记录最终的序列

void dfs(int k) { //层级,序列中已经有了几个数字
    if(k>n){//k==n+1
        for(int i=1;i<=n;i++){
            printf("%5d",a[i]);
        } 
        printf("\n");
        return;
    } 
    for(int i=1;i<=n;i++){
        if(!v[i]){//没有被选中的数字 
            v[i]=1;
            a[k]=i;
            dfs(k+1); 
            v[i]=0;//复原 
            a[k]=0;//可以省略 
        }
    }
    return; 
}

int main() {
    scanf("%d",&n);
    dfs(1);
    return 0;
}

STL:next_permutation

 1 #include<iostream>
 2 #include<algorithm>//使用 next_permutation()和sort()需要导入的头文件 
 3 using namespace std;
 4 int main(){
 5     int a[4]={2,1,4,3};
 6     
 7     sort(a,a+4);//对数组排序 
 8     
 9     do{
10         for(int i=0;i<4;i++){//打印排列 
11             cout<<a[i]<<' ';
12         }
13         cout<<endl;
14     }while(next_permutation(a,a+4));//获取下一个排列 
15 } 

子集生成《算法竞赛入门经典》188页

需要大量做题以后才能够总结、掌握、理解,新课讲授时候可暂时略过

增量构造法

位向量法

二进制法(需要熟练掌握位运算)

P1219. [USACO1.5] 八皇后 Checker Challenge

 1 #include<iostream>
 2 using namespace std;
 3 int n;
 4 int tot=0;
 5 int a[15];
 6 void search(int k) {
 7     if(k>n) {
 8         tot++;//递归的边界,走到这一步必然符合条件
 9         if(tot<4) {
10             for(int i=1; i<=n; i++) {
11                 cout<<a[i]<<' ';
12             }
13             cout<<endl;
14         }
15         return;
16     }
17     for(int i=1; i<=n; i++) {
18         int ok=1;
19         a[k]=i;//把第k行的皇后放在i列
20         int ux=k;
21         int uy=i;
22         for(int j=1; j<k; j++) {
23             int vx=j;
24             int vy=a[j];
25             if(uy==vy||ux+uy==vx+vy||ux-uy==vx-vy) {//列冲突,左、右对角线冲突
26                 ok=0;
27                 break;
28             }
29         }
30         if(ok) search(k+1);
31     }
32     return;
33 }
34 int main() {
35     cin>>n;
36     search(1);
37     cout<<tot;
38     return 0;
39 }

 

 1 #include<iostream>
 2 using namespace std;
 3 int n;
 4 int tot=0;
 5 int a[15];
 6 int vy[30],vl[30],vr[30];
 7 void search(int k) {
 8     if(k>n) {
 9         tot++;//递归的边界,走到这一步必然符合条件
10         if(tot<4) {
11             for(int i=1; i<=n; i++) {
12                 cout<<a[i]<<' ';
13             }
14             cout<<endl;
15         }
16         return;
17     }
18     for(int i=1; i<=n; i++) {
19         int x=k;
20         int y=i;
21         if(!vy[y]&&!vl[x+y]&&!vr[x-y+n]) {
22             a[k]=i;
23             vy[y]=1;
24             vl[x+y]=1;
25             vr[x-y+n]=1;
26             search(k+1);
27             vy[y]=0;
28             vl[x+y]=0;
29             vr[x-y+n]=0;
30         }
31     }
32     return;
33 }
34 int main() {
35     cin>>n;
36     search(1);
37     cout<<tot;
38     return 0;
39 }

 

素数环 Prime Ring Problem

https://www.luogu.com.cn/problem/UVA524

#include<iostream>
#include<cstring>
using namespace std;
int n,tot;
int a[20],v[20];
void print() {
    for(int i=1; i<n; i++) {
        cout<<a[i]<<' ';
    }
    cout<<a[n]<<endl;
}
bool is_prime(int x) {
    if(x<=1) return false;
    for(int i=2; i*i<=x; i++) {
        if(x%i==0) return false;
    }
    return true;
}

void prime_ring(int k) {
    if(k>n) {
        if(is_prime(a[1]+a[n])) print();
        return;
    }
    for(int i=1; i<=n; i++) {
        if(!v[i]&&(k==1||is_prime(a[k-1]+i))) {
            v[i]=1;
            a[k]=i;
            prime_ring(k+1);
            v[i]=0;
        }
    }
    return;
}

int main() {
    a[1]=1;
    v[1]=1;
    while(cin>>n) {
        if(tot>=1)
            cout<<endl;
        tot++;
        cout<<"Case "<<tot<<':'<<endl;
        prime_ring(2);
    }
    return 0;
}

 

P2404 自然数的拆分问题

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<iomanip>
 4 using namespace std;
 5 int n;
 6 int a[10];
 7 void print(int k) {
 8     printf("%d",a[1]);
 9     for(int i=2; i<k; i++) {
10         printf("+%d",a[i]);
11     }
12     printf("\n");
13 }
14 
15 void work(int x,int k) {
16     if(x==0) {
17         if(k!=2) print(k);
18         return;
19     }
20     for(int i=a[k-1]; i<=x; i++) {
21         x-=i;
22         a[k]=i;
23         work(x,k+1);
24         x+=i;
25     }
26     return;
27 }
28 int main() {
29     cin>>n;
30     a[0]=1;
31     work(n,1);
32     return 0;
33 }

 

P2392 kkksc03考前临时抱佛脚

深入浅出程序设计竞赛192

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 using namespace std;
 5 int s[5][25];
 6 int tot=0,t1=1,t2=0;
 7 int mt=60*20+10;
 8 void search(int x,int k) {
 9     if(k>s[x][0]) {
10     //    cout<<"t1="<<t1<<" t2="<<t2<<endl;
11         mt=min(max(t1,t2),mt);
12         return;
13     }
14     t1+=s[x][k];
15     search(x,k+1);
16     t1-=s[x][k];
17 
18     t2+=s[x][k];
19     search(x,k+1);
20     t2-=s[x][k];
21     return;
22 }
23 
24 int main() {
25     for(int i=1; i<=4; i++) cin>>s[i][0];
26     for(int i=1; i<=4; i++) {
27         for(int j=1; j<=s[i][0]; j++) {
28             cin>>s[i][j];
29         }
30     }
31     for(int i=1; i<=4; i++) {
32         t1=0;
33         t2=0;
34         mt=60*20+10;
35         search(i,1);
36         //cout<<mt<<endl;
37         tot+=mt;
38     }
39     cout<<tot;
40     return 0;
41 }

优化方向:选择了一些题目,但是这些题目的总耗时超过了所有题目总耗时的一半,那这些题目的集合就不合法,没有继续搜索下去的必要了。

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int s[5][25],sum[5];
int tot=0,t1=1;
int mt=60*20+10;
void search(int x,int k) {
    if(k>s[x][0]) {
        //    cout<<"t1="<<t1<<" t2="<<t2<<endl;
        mt=min(max(t1,sum[x]-t1),mt);
        return;
    }
    //
    if(t1+s[x][k]<=sum[x]/2) {
        t1+=s[x][k];
        search(x,k+1);
        t1-=s[x][k];
    }
    //不选
    search(x,k+1); 
    return;
}

int main() {
    for(int i=1; i<=4; i++) cin>>s[i][0];
    for(int i=1; i<=4; i++) {
        for(int j=1; j<=s[i][0]; j++) {
            cin>>s[i][j];
            sum[i]+=s[i][j];
        }
    }
    for(int i=1; i<=4; i++) {
        t1=0;
        mt=60*20+10;
        search(i,1);
        //cout<<mt<<endl;
        tot+=mt;
    }
    cout<<tot;
    return 0;
}

 

其他题目:

C000004. 马走日

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<iomanip>
 4 #include<cstring>
 5 using namespace std;
 6 int t,n,m,v[15][15];
 7 int x,y,tot;
 8 int dir[8][2]= {{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}};
 9 bool in_map(int x,int y) {
10     if(x >= 0 && x < n && y >= 0 && y < m) return true;
11     else return false;
12 }
13 
14 void search(int x,int y,int k) {
15     if(k==0) {
16         tot++;
17         return;
18     }
19     for(int i=0; i<8; i++) {
20         //int nx=x+dx[i];
21         //int ny=y+dy[i];
22         int nx = x + dir[i][0], ny = y + dir[i][1];
23         if(in_map(nx,ny)&&!v[nx][ny]) {
24             v[nx][ny]=1;
25             search(nx,ny,k-1);
26             v[nx][ny]=0;
27         //    cout<<nx<<ny<<endl; 
28         }
29     }
30     return;
31 }
32 
33 int main() {
34     cin>>t;
35     while(t--) {
36         cin>>n>>m>>x>>y;
37         tot=0;
38         memset(v,0,sizeof v);
39         v[x][y]=1;
40         search(x,y,n*m-1);
41         cout<<tot<<endl;
42     }
43     return 0;
44 }

P2386. Lake Counting

挑战程序设计竞赛33页

从任意的W开始,不停地把邻接的部分用.代替。1次DFS以后与初始的这个W连接的所有W就都被替换成了. 。因此直到图中不再存在W为止,总共进行的DFS的次数就是答案了。

8个方向总共对应了8种转移状态,每个格子作为DFS的参数至多被调用一次,所以复杂度为O(8*n*m).

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 using namespace std;
 5 int n,m,ans=0;
 6 int a[110][110];
 7 int dx[10]= {0,-1,-1,-1,0,0,1,1,1};
 8 int dy[10]= {0,-1,0,1,-1,1,-1,0,1};
 9 void dfs(int x,int y) {
10     for(int i=1; i<=8; i++) {
11         int nx=x+dx[i];
12         int ny=y+dy[i];
13         if(a[nx][ny]==1) {
14             a[nx][ny]=0;
15             dfs(nx,ny);
16         }
17     }
18     return;
19 }
20 
21 int main() {
22     cin>>n>>m;
23     for(int i=1; i<=n; i++) {
24         for(int j=1; j<=m; j++) {
25             char c;
26             cin>>c;
27             if(c=='.') a[i][j]=0;//旱地
28             else a[i][j]=1;//水坑
29         }
30     }
31     for(int i=1; i<=n; i++) {
32         for(int j=1; j<=m; j++) {
33             if(a[i][j]==1) {
34                 ans++;
35                 dfs(i,j);
36             }
37         }
38     }
39     cout<<ans;
40     return 0;
41 }

 

困难的串 Krypton Factor

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int n,l,tot=1;
 6 string s;
 7 bool ok(string x) {
 8     int xl=x.length();
 9     for(int i=1; i<=xl/2; i++) {
10         int sl=xl-2*i;
11         int sr=xl-i;
12         int flag=1;//默认是容易的串
13         //cout<<"i="<<i<<" sl="<<sl<<" sr="<<sr<<endl;
14         for(int j=0; j<i; j++) {
15             if(x[sl+j]!=x[sr+j]) {
16                 flag=0;
17                 break;
18             }
19         }
20         if(flag==1) return false;
21     }
22     return true;
23 }
24 
25 void print(string t) {
26     int len=t.length();
27     for(int i=1; i<=len; i++) {
28         printf("%c",t[i-1]);
29         if(i%4==0&&i%64!=0&&i!=len ) cout<<" ";
30         if(i%64==0&&i!=len) cout<<endl;
31     }
32     printf("\n");
33     printf("%d\n",len);
34 }
35 void dfs(string c) {
36     if(tot>n) return;
37     for(int i=0; i<l; i++) {
38         string t=c+char(i+'A');
39         //    cout<<"try  "<<t<<endl;
40         if(ok(t)) { //困难的串
41             if(tot==n) {
42                 print(t);
43             }
44             tot++;
45             dfs(t);
46         }
47     }
48     return;
49 }
50 
51 int main() {
52     while(1) {
53         cin>>n>>l;
54         tot=1;
55         s="";
56         if(n==0&&l==0) break;
57         dfs(s);
58 //    cout<<ok("ABAC");
59     }
60     return 0;
61 }

 P1123 取数游戏

 1 //P1123 取数游戏
 2 #include<iostream>
 3 #include<queue>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 const int N=10;
 8 int t,n,m,ans;
 9 int g[N][N],vis[N][N];
10 int dx[]= {-1,-1,-1,0,0,1,1,1};
11 int dy[]= {-1,0,1,-1,1,-1,0,1};
12 void dfs(int x,int y,int sum) {
13     if(y>m) {//换行
14         x++;
15         y=1;
16     }
17     if(x==n+1) {//出口
18         ans=max(ans,sum);
19         return;
20     }
21     //不选
22     dfs(x,y+1,sum);
23     //
24     int f=1;
25     for(int i=0; i<8; i++) {
26         int tx=x+dx[i];
27         int ty=y+dy[i];
28         if(vis[tx][ty]==1) {
29             f=0;
30             break;
31         }
32     }
33     if(f) { //格子可以选
34         vis[x][y]=1;
35         dfs(x,y+1,sum+g[x][y]);
36         vis[x][y]=0;
37     }
38     return;
39 }
40 
41 int main() {
42     cin>>t;
43     while(t--) {
44         cin>>n>>m;
45         for(int i=1; i<=n; i++) {
46             for(int j=1; j<=m; j++) {
47                 cin>>g[i][j];
48             }
49         }
50         memset(vis,0,sizeof vis);
51         ans=0;
52         dfs(1,1,0);//第一个格子
53         cout<<ans<<endl;
54     }
55     return 0;
56 }

 P2196 [NOIP1996 提高组] 挖地雷

 1 //P2196 [NOIP1996 提高组] 挖地雷
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cmath>
 5 using namespace std;
 6 const int N=15;
 7 int n;
 8 int g[N][N],a[N];
 9 int ans=0,c[N],num=0;
10 int b[N],vis[N];
11 
12 void dfs(int x,int idx,int sum) {
13     if(sum>ans) {
14         ans=sum;
15         num=idx;
16         for(int i=0; i<idx; i++) {
17             c[i]=b[i];
18         }
19     }
20     for(int i=1; i<=n; i++) {
21         if(vis[i]==0&&g[x][i]==1) {
22             vis[i]=1;
23             b[idx]=i;
24             dfs(i,idx+1,sum+a[i]);
25             vis[i]=0;
26             b[idx]=0;
27         }
28     }
29     return;
30 }
31 
32 int main() {
33     cin>>n;
34     for(int i=1; i<=n; i++) cin>>a[i];//每个地窖中的地雷
35     for(int i=1; i<=n; i++) {
36         for(int j=i+1; j<=n; j++) {
37             int b;
38             cin>>b;
39             g[i][j]=b;
40         }
41     }
42     for(int i=1; i<=n; i++) {
43         vis[i]=1;
44         b[0]=i;
45         dfs(i,1,a[i]);
46         b[0]=0;
47         vis[i]=0;
48     }
49     for(int i=0; i<num; i++) {
50         cout<<c[i]<<' ';
51     }
52     cout<<endl<<ans<<endl;
53     return 0;
54 }

 

posted @ 2023-06-27 10:14  关于42号星球  阅读(177)  评论(0)    收藏  举报