计算思维实践之路(八)-深度优先搜索

             2月19日,多云。“小楼一夜听春雨,深巷明朝卖杏花“。

       例1:输入一个数n,按字典序从小到大的顺序输出1~n的全排列。两个序列的字典序大小关系等价于从开始的第一个不相同位置处的大小关系。例如,(1,3,2) <  (2,1,3)    
       当n = 3时,所有排序的排序结果是(1,2,3)、(1,3,2)、(2,1,3)、(2,3,1)、(3,1,2)、(3,2,1)。

       俺老猪擅长穷举法寻找问题解的一种可靠的方法是首先列出所有可能候选解,然后依次检查每一个,在检查完所有或部分候选解后,即可找到所需要的解。理论上,当候选解数量有限并且通过检查所有或部分候选解能够得到所需解时,上述方法是可行的。不过在实际应用中,很少使用这种方法,因为候选解的数量通常都非常大,即便采用最快的计算机也只能解决规模很小的问题。

#include <cstring>
#include <cstdio>
#include <iostream>

using namespace std;

int main()
{
    int a,b,c;
    for(a=1; a<=3; a++)
        for(b=1; b<=3; b++)
            for(c=1; c<=3; c++)
                if((a!=b)&&(a!=c)&&(b!=c))
                    cout<<a<<" "<<b<<" "<<c<<endl;
    return 0;
}

输出:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
     八戒,师傅再告诉你一种新的方法-回溯法。
         从问题的某一种可能出发,搜索从这种情况出发所能达到的所有可能, 当这一条路走到“ 尽头 ”而没达到目的地的时候, 再倒回上一个出发点, 从另一个可能出发, 继续搜索. 这种不断“倒回 一步"寻找解的方法, 称作" 回溯法 "。
        为师给出算法框架,悟空,还是你来实现以下。
1. procedure dfs ( step:integer )
2. begin
3.    if 到达边界状态 then 输出解 ;  
4.    else for  i := 1 to n do // 顺次尝试每一种可能
5.       begin
6.            if 满足条件 then
7.              begin
8.                   保存结果 ;
9.                   dfs (step+1);
10.                   恢复:保存结果之前的状态
11.               end;
12.        end;
13. end;
14. Begin
15.      dfs (1);
16. end.
        师傅,让俺老孙试一试吧。
#include <cstring>
#include <cstdio>
#include <iostream>

using namespace std;

int a[20];
bool used[20];
int n;

void print()
{
    for(int i=1; i<=n; i++)
        cout<<a[i]<<" " ;
    cout<<endl;
}

void dfs(int step)
{
    if (step ==(n + 1))
        print();
    else
    {
        for(int i=1; i<=n; i++)
        {
            if (used[i] == false)
            {
                a[step] = i;
                used[i] = true;
                dfs(step+1);
                used[i] = false;
            }
        }
    }


}
int main()
{
    memset(used, false, sizeof(used));
    while(scanf("%d", &n) == 1)
    {
        dfs(1);
        cout<<endl;
    }
    return 0;
}
结果:
2
1 2
2 1

3
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
       

       大家都知道,深度优先的主要思想就是“不撞南墙不回头”,“一条路走到黑”,如果遇到“墙”或者“无路可走”时再去走下一条路。所以先规定好一个走路的规则,比如就按照右下左上顺时针的方式去尝试。

             HDU 1016 Prime Ring Problem是一道简单的搜索题,简单说就是一位一位去试,一位一位地填上数字,看是否满足条件,具体看注释。
        俺沙悟净也试一试
#include <cstring>
#include <cstdio>

using namespace std;

// 素数表,数据较小直接打表也可以
bool prime[41];
void init()
{
    memset(prime, true, sizeof(prime));
    prime[0] = prime[1] = false;
    for(int i=2; i<=40; i++)
    {
        if(prime[i])
        {
            for(int j=i*2; j<=40; j+=i)
                prime[j] = false;
        }
    }
}

// 标记
bool vis[21];
int num[21];
int N, t=1;
void dfs(int n)
{
    if(n == N && prime[1+num[n]])
    {
        // 判断最后一位是否满足条件,打印
        printf("1");
        for(int i=2; i<=N; i++)
            printf(" %d", num[i]);
        printf("\n");
    }
    else
    {
        // 在还未访问的数据中选择满足条件的,标记并访问
        for(int i=2; i<=N; i++)
        {
            if(!vis[i] && prime[i+num[n]])
            {
                num[n+1] = i;
                vis[i] = true;
                dfs(n+1);
                // 访问结束,重新设为未访问
                vis[i] = false;
            }
        }
    }
}

int main()
{
    init();
    memset(vis, false, sizeof(vis));
    vis[1] = true;
    num[1] = 1;

    while(scanf("%d", &N) == 1)
    {
        printf("Case %d:\n", t++);
        dfs(1);
        printf("\n");
    }
    return 0;
}
    俺白龙马也试一试
#include<iostream>
using namespace std;
int P[38]= {0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1};
// 素数表,数据较小直接打表
int visited[21];
int a[21];
int n;
void DFS(int count)
{
    int i;
    if(count==n&&P[a[count-1]+1]==1)// // 判断最后一位是否满足条件,打印
    {
        cout<<a[0];
        for( i=1; i<n; i++)
        {
            cout<<' '<<a[i];
        }
        cout<<endl;
    }
    else
    {
        for(i=2; i<=n; i++)
        {
            if(!visited[i]&&P[a[count-1]+i]==1)//在还未访问的数据中选择满足条件的,标记并访问
            {
                a[count]=i;
                visited[i]=1;
                DFS(count+1);
                visited[i]=0; // 访问结束,重新设为未访问
            }
        }
    }
}
int main()
{
    a[0]=1;
    int A=1;
    while(cin>>n)
    {
        cout<<"Case "<<A++<<':'<<endl;
        DFS(1);
        cout<<endl;
    }
    return 0;
}




     





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值