关于深度优先搜索的练习

对于dfs(深搜)一窍不通的蒟蒻打算改变自己了..

简单的选数

【例1】素数环

【题目描述】从1到20这20个数摆成一个环,要求相邻的两个数的和是一个素数。
【算法分析】
非常明显,这是一道回溯的题目。从1开始,每个空位有20种可能,只要填进去的数合法:与前面的数不相同;与左边相邻 的数的和是一个素数。第20个数还要判断和第1个数的和是否素数。
【算法流程】
1、数据初始化; 2、递归填数:判断第i个数填入是否合法;
A、如果合法:填数;判断是否到达目标(20个已填完):是,打印结果;不是,递归填下一个;
B、如果不合法:选择下一种可能;

#include<cstdio>
#include<cstring>
using namespace std;

const int N=20,MAXN=1002;
int a[MAXN]={1},sum;//把a[0]初始化成1
bool used[MAXN],prime[MAXN];

void print()
{
    sum++;
    for(int i=1;i<=N;i++)
        printf("%d ",a[i]);
    printf("\n");
}
void dfs(int t)
{
    for(int i=1;i<=N;i++)//一共有N个数可供选择
    {
        if(!used[i]&&prime[i+a[t-1]])
        //a[0]=1的作用:t=1时,如果a[0]是0,a[1]就永远不会是1
        //也就是说不会从1开始搜索,而是跳过1,直接搜2
        {
            a[t]=i;
            used[i]=true;
            if(t==20)
                {if(prime[a[20]+a[1]]) print();}//这里一定要加大括号,
                //不然下面的else就和判断第一个和最后一个数加起来是不是素数的if对到一起了
                //也就永远不会递归到下一层
            else dfs(t+1);
            used[i]=false;//回溯
        }
    }
}
int main()
{
    memset(prime,1,sizeof(prime));
    prime[0]=prime[1]=false;
    for(int i=1;i<=2*N-1;i++)//线性筛出素数表,之后查询的复杂度变为O(1)
                            //头和尾的两个数相加最大就是N+N-1,也就是 2*N-1 咯
    {
        if(prime[i])
            for(int j=i<<1;j<=2*N-1;j+=i) prime[j]=false;
    }
    dfs(1);//从1开始搜索
    printf("%d",sum);
}

【例2】排列的输出

【题目描述】设有n个整数的集合{1,2,…,n},从中取出任意r个数进行排列(r < n),试列出所有的排列。
【算法分析】
没有什么好分析的,堪称dfs中最简单的题目。

#include<cstdio>
using namespace std;

int n,r,a[1002],tot=0;
bool used[1002];

void print()
{
    tot++;
    for(int i=1;i<=r;i++)
        printf("%d ",a[i]);
    printf("\n");
}
void dfs(int t)
{
    for(int i=1;i<=n;i++)//从n个数中选
    {
        if(!used[i])
        {
            used[i]=true;
            a[t]=i;
            if(t==r)//选取到r个数就完成一组的排列
                print();
            else dfs(t+1);
            used[i]=false;//回溯
        }
    }
}
int main()
{
    scanf("%d%d",&n,&r);
    dfs(1);
    printf("number:%d",tot);
}

【例3】整数的拆分

【题目描述】任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。
【样例输入】7
【样例输出】
7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
total=14
【算法分析】由样例可知,拆分的整数输出后应该是一个最长不下降子序列,事实上,这样拆分也能同时起到去重的作用。比如2+1+1+1+1和1+1+2+1+1等都是一种情况,也就是1+1+1+1+2。每拆掉一个数,就从n中减掉一个数,到n为0的时候一组拆分完毕,这次打印的时候传递给print函数一个参数,表示拆分出来的数的个数,因为拆分的个数是不确定的。

#include<cstdio>
using namespace std;

int n,a[1002]={1},tot,k;

void print(int t)
{
    tot++;
    printf("%d=",n);
    for(int i=1;i<t;i++)
        printf("%d+",a[i]);
    printf("%d\n",a[t]);
}
void dfs(int t)
{
    for(int i=a[t-1];i<=k;i++)//i<=k保证了k-i>=0
    {
        if(i<n)//从样例可知,不能什么都不拆分输出原整数,所以i不能等于n
        {
            a[t]=i;//记录
            k-=i;
            if(k==0)
                print(t);//表示拆分出来了t个数
                //t其实是表示的递归的层数,而每递归一层,便会记录一个拆分数来的数
                //所以t也是实际上a数组中有效数字的个数
            else dfs(t+1);
            k+=i;//回溯
        }
    }
}
int main()
{
    scanf("%d",&n);
    k=n;//搜索的时候自然是不能从n里面减数的
    dfs(1);
    printf("total=%d",tot);
}
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值