《算法笔记》DFS、BFS

选择问题(DFS)

背包问题(不剪枝)
问题描述:
给你一些物品,以及这些物品的重量和价值,求在不超过最大重量的前提下所选物品的最大价值
#include <iostream>
using namespace std;
const int maxn = 30;
int n, V, maxValue = 0;//n代表物品数量,V代表最大重量, maxValue记录最大价值
int w[maxn], c[maxn];
//index是当前处理物品的编号, sumW是之前已选物品总的重量,sumC是之前已选物品总的价值
void DFS(int index, int sumW, int sumC){
    //不采用回溯剪枝的算法,不论结果怎样,都会进行到最后一个物品的选择
    if(index == n){
        if(sumW <= V && sumC > maxValue){
            maxValue = sumC;
        }
        return;
    }
    DFS(index + 1, sumW , sumC);//代表不选择当前物品
    DFS(index + 1, sumW + w[index], sumC + c[index]);//代表选择当前物品
}

int main()
{
    scanf("%d%d", &n, &V);
    for(int  i = 0; i < n; i ++) scanf("%d", &w[i]);
    for(int  i = 0; i < n; i ++) scanf("%d", &c[i]);
    DFS(0, 0, 0);
    cout << maxValue;
    system("pause");
    return 0;
}

背包问题(剪枝)
#include <iostream>
using namespace std;
const int maxn = 30;
int n, V, maxValue = 0;//n代表物品数量,V代表最大重量, maxValue记录最大价值
int w[maxn], c[maxn];
//index是当前处理物品的编号, sumW是之前已选物品总的重量,sumC是之前已选物品总的价值
void DFS(int index, int sumW, int sumC){
    //采用回溯剪枝的算法
    if(index == n) return;
    DFS(index + 1, sumW , sumC);//代表不选择当前物品
    //只有选择当前物品不超过最大重量时,才进行选择的后续步骤
    if(sumW + w[index] <= V){
        //如果超过最大值,则更新最大值
        if(sumC + c[index] > maxValue) maxValue = sumC + c[index];
        DFS(index + 1, sumW + w[index], sumC + c[index]);
    }
}

int main()
{
    scanf("%d%d", &n, &V);
    for(int  i = 0; i < n; i ++) scanf("%d", &w[i]);
    for(int  i = 0; i < n; i ++) scanf("%d", &c[i]);
    DFS(0, 0, 0);
    cout << maxValue;
    system("pause");
    return 0;
}
还有另一种剪枝算法,就是不预先判断后果,直接进行选择,选择后在进行剪枝,代码可能会好理解一点,
就跟判断树的叶节点一样,如果当前节点为空,就证明已经是叶节点的下一层了,直接返回,而不是去判断他有没有孩子节点,
这样虽然会多循环一下,但理解起来好理解一些
代码如下,注意因为是后剪枝,所以在判断index == n之前,应该更新一下上一次留下的结果:
void DFS(int index, int sumW, int sumC){
//采用回溯剪枝的算法
if(sumW > V)return;
if(sumC > maxValue) maxValue = sumC;
if(index == n) return;
DFS(index + 1, sumW , sumC);//代表不选择当前物品
DFS(index + 1, sumW + w[index], sumC + c[index]);//代表选择当前物品
}
求解最优子序列问题
问题描述:给定一个序列,求这个问题的所有子序列,从中选择最优子序列;等价于从N个数中选择K个数的所有方案;
给定N个整数,从中选择K个数,令其和为X,若存在多种情况,返回平方和最大的一组。
#include <iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
const int maxn = 30;
int n, k, x, maxSumSqu = -1, A[maxn];
vector<int> temp, ans;//temp记录每一种临时情况,ans记录最优情况
//index代表当前处理元素的下标, nowk记录一选择元素个数, sum代表已选元素的总和,sumSqu代表平方和
void DFS(int index, int nowK, int sum, int sumSqu){
    //对于后剪枝问题,每次循环前要先判断上次的结果是不是最大值
    if(nowK == k &&  sum == x){
        if(sumSqu > maxSumSqu){
            maxSumSqu = sumSqu;
            ans = temp;
            return;
        }
    }
    //如果已经处理完n个元素 或者 总和大于x 或者 总个数大于k,则返回
    if(index == n || sum > x || nowK > k) return;
    //不选index号元素
    DFS(index + 1, nowK, sum, sumSqu);
    //选择index元素
    temp.push_back(A[index]);//将A[index]放入temp中
    //如果一个元素可以选多次,这句话改为:
    //DFS(index , nowK +1, sum + A[index], sumSqu + A[index] * A[index]);
    DFS(index +1, nowK +1, sum + A[index], sumSqu + A[index] * A[index]);
    temp.pop_back();//将A[index]从temp中弹出
}
int main()
{
    scanf("%d%d%d", &n, &k, &x);
    for(int  i = 0; i < n; i ++) scanf("%d", &A[i]);
    DFS(0, 0, 0, 0);
    cout << maxSumSqu <<endl;
    for(int i = 0; i < ans.size(); i ++) cout << ans[i] <<' ';
    system("pause");
    return 0;
}

遍历问题

求连通块问题(BFS)
如果要保证每个元素只能被访问一次,则不论是DFS还是BFS都要额外开一个数组存储当前节点是否被处理过;
BFS遍历时判断的依据是节点有没有入过队;DFS遍历时判断的依据是节点有没有被DFS过;
如果遍历一遍所有元素用DFS进行后剪枝代码比较简洁,不需要自己维护栈;
如果是要求到达终点的最少步数,则用BFS简单;如果用DFS则需要找到所有能到达终点的路径,并进行比较,运算量很大,且每个节点需要多次访问;
#include <iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 30;
int m, n, matrix[maxn][maxn];
bool iqu[maxn][maxn] = {false};//iqu存储是否入过队,false没入过,true代表入过
int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};
struct node{
    int x, y;
};
//BFS 不是用递归实现的,所以不需要显式写出结束条件,但是需要将当前节点入队,作为第一个元素;
void BFS(int x, int y){
    queue<node> q;
    q.push({x, y});
    iqu[x][y] = true;
    //当队列为空后,迭代结束
    while(!q.empty()){
        node temp = q.front();
        q.pop();
        for(int  i = 0; i < 4; i ++){
            int x0 = temp.x + dx[i], y0 = temp.y + dy[i];
            if(x0 < 0 || x0 >= m || y0 < 0 || y0 >= n) continue;//对于涉及到下标的问题,先看下标是否合法
            //在下标合法的情况下,如果位置是1,并且没有入过队,才继续进行
            if(matrix[x0][y0] == 1 && iqu[x0][y0] == false){
                q.push({x0, y0});
                iqu[x0][y0] = true;
            }
        }
    }
    return;
}

int main()
{
    int ans = 0;
    //读入矩阵
    scanf("%d%d", &m, &n);
    for(int  i = 0; i < m; i ++){
        for(int  j = 0; j < n; j ++){
            scanf("%d", &matrix[i][j]);
        }
    }
    //遍历矩阵
    for(int  i = 0; i < m; i ++){
        for(int j = 0; j < n; j ++){
            if(matrix[i][j] == 1 && iqu[i][j] == false){
                ans++;
                BFS(i, j);
            }
        }
    }
    cout << ans;
    system("pause");
    return 0;
}

求连通块问题(DFS)
#include <iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 30;
int m, n, matrix[maxn][maxn];
bool check[maxn][maxn] = {false};//check代表是否检查过
int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};

//DFS进行后剪枝,如果下标不合法,或者不是1,或者已经检查过,则返回
void DFS(int x0, int y0){
    //下标不合法,则返回
    if(x0 < 0 || x0 >= m || y0 < 0 || y0 >= n) return;
    //这个数据不是1,或者已经处理过,则返回
    if(matrix[x0][y0] != 1 || check[x0][y0] == true) return;

    check[x0][y0] = true;//将当前元素设置为已检查
    for(int  i = 0; i < 4; i ++){
        int x = x0 + dx[i], y = y0 + dy[i];
        DFS(x, y);
    }
    return;
}

int main()
{
    int ans = 0;
    //读入矩阵
    scanf("%d%d", &m, &n);
    for(int  i = 0; i < m; i ++){
        for(int  j = 0; j < n; j ++){
            scanf("%d", &matrix[i][j]);
        }
    }
    //遍历矩阵
    for(int  i = 0; i < m; i ++){
        for(int j = 0; j < n; j ++){
            if(matrix[i][j] == 1 && check[i][j] == false){
                ans++;
                DFS(i, j);
            }
        }
    }
    cout << ans;
    system("pause");
    return 0;
}

求最少步数(BFS)
#include <iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 30;
int m, n, ans = -1;
char matrix[maxn][maxn];
bool check[maxn][maxn] = {false};//check代表是否入过队
int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};

struct node{
    int x, y, step;//step表示第几步到达它
}S, T;

void BFS(){
    queue<node> q;
    q.push(S);
    check[S.x][S.y] = true;//开始点入队后要设为已入队
    while(!q.empty()){
        node top = q.front() ;
        q.pop();
        //判断队首元素是不是终点
        if(top.x == T.x && top.y == T.y){
            ans = top.step;
            break;
        }
        for(int  i =0 ; i < 4; i ++){
            int x = top.x + dx[i], y =top.y + dy[i];
            if(x < 0 || x >= m || y < 0 || y >= n) continue;//如果下标不满足,直接continue
            //如果不是墙且没有入过队则入队
            if(matrix[x][y] != '*' && check[x][y] == false){
                node temp = {x, y, top.step + 1};
                q.push(temp);
                check[x][y] = true;
            }
        }
    }
    return ;
}

int main()
{
    //读入矩阵
    scanf("%d%d", &m, &n);
    for(int  i = 0; i < m; i ++){
        getchar();//过滤回车
        for(int  j = 0; j < n; j ++){
            scanf("%c", &matrix[i][j]);
        }
    }
    scanf("%d%d%d%d", &S.x, &S.y, &T.x, &T.y);
    S.step = 0;
    //进行BFS
    BFS();
    cout << ans;
    system("pause");
    return 0;
}

求最少步数(DFS)
#include <iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 30;
int m, n, matrix[maxn][maxn];
bool check[maxn][maxn] = {false};//check代表是否检查过
int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};
int ans = -1;
struct node{
    int x, y;
}S, T;

//DFS进行后剪枝,如果下标不合法,或者不是1,或者已经检查过,则返回
void DFS(int x0, int y0, int step){
    //下标不合法,则返回
    if(x0 < 0 || x0 >= m || y0 < 0 || y0 >= n) return;
    if(matrix[x0][y0] == '*') return;
    //如果这个点恰好是出口,跟当前全局步数比较大小
    if(x0 == T.x && y0 == T.y){
        ans = max(step, ans);
        return;
    }
    for(int  i = 0; i < 4; i ++){
        int x = x0 + dx[i], y = y0 + dy[i];
        DFS(x, y, step + 1);
    }
    return;
}

int main()
{
    //读入矩阵
    scanf("%d%d", &m, &n);
    for(int  i = 0; i < m; i ++){
        getchar();//过滤回车
        for(int  j = 0; j < n; j ++){
            scanf("%c", &matrix[i][j]);
        }
    }
    scanf("%d%d%d%d", &S.x, &S.y, &T.x, &T.y);
    //进行DFS
    DFS(S.x, S.y, 0);
    cout << ans;
    system("pause");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值