深度优先搜索练习
关于凑数 (参考书目《啊哈!算法》)
从1-9共9张扑克牌中放到9个盒子里,并使得____+____=____成立(每个下划线上填一个三位数)
#include<stdio.h>
int a[10],book[10],total;
//这里还有需要注意的地方C语言全局变量默认为0
void dfs(int step){
int i;
if(step==10)//设立终止点
{
if(a[1]*100+a[2]*10+a[3]+a[4]*100+a[5]*10+a[6]==a[7]*100+a[8]*10+a[9]){
total++;
printf("%d%d%d+%d%d%d=%d%d%d\n",a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]);
}
return ;
}
for(int i=1;i<=9;i++){
if(book[i]==0){ //说明i号扑克牌还在手里,需要放入step号盒子
a[step]=i;//将i号扑克牌放到第step个盒子中
book[i]=1;//此时i号扑克牌已经被使用
dfs(step+1);
//并且step在下一步中每次调用都会自动加1
book[i]=0;
}
}
return;//这里表示这一级别的dfs函数已经结束了,返回上一级 dfs函数
}
int main(){
dfs(1); //dfs函数的开始
printf("%d",total/2);
//之所以除以2是因为会出现173+286=459和286+173=459是同一种解的情况
return 0;
}
为了更方便的理解,我们可以尝试先自己先演示一次。例如:123+456=789,显然是不成立的。程序会直接return,退回上一级,即123+345=78_,而此时循环已经结束,所以又会退回更上一级,123+456=7__。本轮中 i 此时等于8,循环进入下一级,i=9, 此时123+456=79_。执行dfs(9),得到123+456=798。不成立,按照上述步骤继续重复往更上一级后退 。
有了上面的经验,我们继续进行下一道题
求部分和
给定质整数a1,a2,…an,判断是否可以从中选出若干数,使他们的和恰好为k.
样例输入:
n=4, k=13 a={1,2,4,7}
输出
YES
2,4,7
输入
n=4, k=15 a={1,2,4,7}
输出
NO
#include<stdio.h>
#include<string.h>
int a[30],book[30];// book[i]用来判断a[i]是否使用了
int n,s,sum,judge,judgeT;//全局变量默认为0
void dfs(int step)
{
int i;
if(s==sum)
{
judge=1;
printf("YES\n");
for(i=0; i<n; i++)
if(book[i])
printf("%d ",a[i]);
printf("\n");
return;
}
if(s>sum){
judgeT=1;
//减少运算量
}
int num;
if(judgeT==0){
for(num=step; num<n; num++)
{
s=s+a[num];
book[num]=1;
dfs(num+1);//这个与上一题不一样的地方在于可能会出现num+1之后大于n的情况,但是不影响程序运行
s= s-a[num];//当程序运算到这一步说明并未出现s==sum的情况,s的值需要退回上一级。
book[num]=0;
judgeT=0;
}
}
}
int main(void)
{
int i;
while(~scanf("%d%d",&n,&sum))//~scanf等效于scanf()!=EOF。没有输入的时候自动退出。
{
judge=0;
memset(book,0,sizeof(book));
//将所有book[]都设置为0 ,这段代码纯画蛇添足(我只是用来复习一下memset)
for(i=0; i<n; i++)
scanf("%d",&a[i]);
dfs(0);
if(judge==0) printf("NO\n");
}
return 0;
}
这道题的关键是在于不是所有输入的数字都会被最终使用,所以这道题的判断终止标准与上一道题截然不同。我觉得只需要在每次调用dfs时使用return语句即可(目前我没有看出来有什么不妥当的地方)。本题中我认为最关键的一步思考是在于s= s-a[num];
同时judgeT用来减少计算量也是很重要的一步。
`
总结:
深度优先搜索重点在于思考“当前情况下如何去做”,至于“下一步如何去做”则与“当前情况下如何去做”有着共同之处
深度优先搜索基本模型
void dfs(int step){
判断边界(可以灵活调整判断边界代码的位置但是必须要有)
通用型尝试每一种可能 for(i=0;i<n;i++){
继续下一步 dfs(step+1)
}
return;
}