对于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);
}