201902-DFS入门失败

对于代码编写不太有帮助的图解就不放了,模板暂且放在这里,思考几个问题

void dfs(状态x)//参数用来表示状态  
{  
    if(到达终点)    {  
        ...//根据题意添加  
        return;  
    }  
    if(不合法)  
        return;  
    if(特殊状态)//剪枝
        return ;
    for(扩展)    {  
        if(扩展所达到状态合法)        {  
            /*修改*/    //根据题意来添加  
            /*标记*/  
            dfs(状态x+δ);  
            /*还原标记*/  
            //是否还原标记根据题意  
            //如果加上(还原标记)就是 回溯法  
        }  
 
    }  
}  

一个问题是否只有一棵树?然后遍历这棵树就行了吗?

一个具体问题的"状态"到底是谁?它又是以怎样的形式作为树的结点?

dfs()的参数代表谁?一个还是多个?

vis[]数组确实有"标记此位置是否访问过"的意义,它的下标又有什么含义?

一个dfs程序被编写之前,我想,这些问题必须被阐明说清,否则仅仅摆出一段代码结果是无意义的。

对于4皇后问题:4x4的网格放置棋子,不允许同行 同列 主对角 副对角 存在其他棋子,要构造这个解空间树

前人给出的想法是:树的层数 代表格子行数(同时也是下棋步数),节点提供的信息 是这行棋子放置的位置

因此给出以下的形式,长度为4的一维数组,4个元素自然依次代表了4次放置棋子的列数,数组被填满时即成为了最后的一个解position[current]

来自B站av12019553

由于是逐行摆放1个,因此不用考虑同行互斥,在代码中要判断同列、主副对角线的互斥,所以需要一个记录数组visited[3][1],

当第一个维度为0,其第二维度的信息 显示同列是否互斥;当第一个维度为1,其第二维度的信息 显示主对角线是否互斥;当第一个维度为2,其第二维度的信息 显示副对角线是否互斥;//暂且接受

这样一来,遍历时判断互斥仅需检查vis[0|1|2][i|cur+i|cur-i+n]为0还是1,i为列数,行列之和恒定 则在同一主对角,行列之差恒定 则在同一副对角;//暂且接受

#include<bits/stdc++.h>
using namespace std;
int cur,pos[4],vis[3][1];
void dfs(int cur){
    if(cur==4){//已经下了4颗,得到一个解,递归出口
        /*输出结果*/
    }else{
        for(int i=0;i<n;i++){//每行自左向右遍历,i为列数
            /*若无互斥情况*/
            if(!vis[0][i]&&!vis[1][i+cur]&&!vis[2][cur-i+n]){
                pos[cur]=i;//将列i填入答案数组
                /*互斥记录*/
                vis[0][i]=1;
                vis[1][i+cur]=1;
                vis[2][cur-i+n]=1;

                /*开始下一行的求解,递归调用dfs*/
                dfs(cur+1);//此时应该已经得到了一个解

                /*将互斥记录归零,准备下一个解*/
                vis[0][i]=0;
                vis[1][i+cur]=0;
                vis[2][cur-i+n]=0;
            }
        }
    }
}

在这一问题中仅有一个解空间树,遍历它即可获得答案;它的每个"状态"是每一步(行)棋下的列数,它以数组的形式作为树的结点;dfs()的参数代表步数(行数);vis[]数组的下标含义比较复杂,上文中已经给出。

在此我已经回答了开头提出的全部问题,连循环计数变量的意义也写在了注释里,画部分需要思考,多希望其他人其他题目的题解,也能对我这种真入门的小白给出明确的答案,而不仅仅是一边喊着我好菜一边只把代码留下

我这样的普通人平凡人一般人,仅仅接触一道例题 是绝对不足以掌握它去解决新问题的。给出另一道

所谓“绝对没有比这更基础的题了”里面的第1题:全排列问题:给出整数N,求出1到N的全排列。

要构造这个树,仿照上一题,树的层数 代表选数次数,节点提供的信息 是这次选择的数字,依旧假定最终的答案是长度为n的1维数组,只需要依次填入当前选择的数,填满数组即得到答案permutation[current]

由于每次选数不能是之前选过的数,所以需要一个记录数组visited[N]

#include<bits/stdc++.h>
using namespace std;
//全排列问题:给出整数n,求出1到n的全排列。n<10
int N,cur,perm[10],vis[10];

void dfs(int cur){
    if(cur==N+1){//已经选过了n个数,得到一个解
        /*输出结果*/
    }else{
        for(int i=1;i<=N;i++){//从1开始尝试选数
            /*若i未被选过*/
            if(!vis[i]){
                perm[cur]=i;//将数i填入答案数组
                /*选数记录*/
                vis[i]=1;
                /*开始下一行的dfs*/
                dfs(cur+1);//此时应该已经得到了一个解
                /*将记录归零,准备下一个解*/
                vis[i]=0;
            }
        }
    }
}

 相对于上一题,这题不需要费心设计访问记录,形式几乎一样,是上一题的精简版;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值