DFS中排列问题

        算法中,dfs,bfs是常见的考点,虽然效率不高,但简单有效。其中数的排列问题也是十分常见,堆循环也能解决,但dfs更加便捷,剪枝,回溯也能提高算法效率(虽然只提高那么一点,该超时还是会超时)

一.排列类型

1.递归实现指数型枚举

#include <iostream>
#include <vector>

using namespace std;
int n;
int book[25];//标记数组,选择为1,否则为2
int arr[25];

void dfs(int index)
{
    if(index > n){
        for(int i=1;i<=n;i++){
            if(book[i]==1){
                cout<<i<<" ";
            }
        }
        puts("");
        return;
    }

    book[index] = 1;//选择index
    dfs(index+1);
    book[index] = 0;//回溯

    book[index] = 2;//不选择
    dfs(index+1);
    book[index] = 0;


}

int main() {

    cin>>n;
    dfs(1);//dfs对1~n进行排列
    return 0;
}

        使用深度优先搜索(DFS)枚举给定范围内数的所有可能状态,并输出了所有可能的组合。具体来说,它是对于给定的 n 个数,每个数有两种状态(即选择和未选择),输出了所有可能的状态组合。在 dfs 函数中,它尝试将每个数标记为 1 或 2,然后递归遍历到下一个数。最后输出了所有可能的状态组合。对于每个数,它都有两种状态(1 或 2),通过递归的方式遍历了所有可能的情况。

2.递归实现组合型枚举

#include <iostream>
#include <vector>

using namespace std;
int n,m;
bool book[25];
vector<int> arr;//存储排列好的组合

void dfs(int index)
{
    if((int)arr.size() == m){
        for(auto i:arr){
            cout<<i<<" ";
        }
        puts("");
        return;
    }

    for(int i=index;i<=n;i++){//保证升序排列
        arr.push_back(i);
        dfs(i+1);
        arr.pop_back();//回溯,清理容器,模拟下一种情况
    }
}

int main() {

    cin>>n>>m;
    dfs(1);
    return 0;
}

        在 dfs 函数中,从当前的索引位置开始,依次选取数字并递归地探索下一个数字,直到达到了组合的长度上限为止。在达到组合长度后,它输出了当前的组合。完成对从 1 到 n中选取长度为 m的所有可能组合进行探索和输出

3.递归实现排列型枚举

#include <iostream>
#include <vector>

using namespace std;
int n;
bool book[25];//标记数组
vector<int> arr;//存储排序组合

void dfs()
{
    if((int)arr.size() == n){
        for(auto i:arr){
            cout<<i<<" ";
        }
        puts("");
        return;
    }

    for(int i=1;i<=n;i++){
        if(!book[i]){//保证不会重复参与排序,即一个数只出现一次
            book[i] = true;
            arr.push_back(i);
            dfs();
            book[i] = false;//回溯
            arr.pop_back();
        }
    }
}

int main() {

    cin>>n;
    dfs();
    return 0;
}

        数组 book 用来标记数字是否被使用过,另外使用了一个 vector<int> arr 用来存储当前生成的排列。具体来说,在 dfs 函数中,它尝试遍历所有可能的情况,从 1 到 n的数字中选取未被使用过的数字。对于每个数字,它标记为已使用(book[i] = true),将其加入当前排列 arr 中,然后递归探索下一个位置。当排列长度达到 n时,输出当前排列并返回。之后,它回溯到上一步,将刚才使用过的数字标记为未使用,继续探索其他可能的排列

二.例题

1.日期统计

#include<bits/stdc++.h>

using namespace std;

int b[]={0,31,28,31,30,31,30,31,31,30,31,30,31};

vector<int> arr;
bool book[50];
int a_f[110];
int x=1;
int a[50];
int date,mon,day;
set<int>q;//用来排重

void dfs(int index)
{
    if( arr.size() == 4 ){
        date = arr[0]*1000+arr[1]*100+arr[2]*10+arr[3];
        mon = arr[0]*10+arr[1];
        day = arr[2]*10+arr[3];
        if(mon > 0 && mon <= 12 && day > 0 && day <= b[mon]){//判定
            q.insert(date);//符合条件加入集合,排除重复日期
        }
        return;
    }

    for(int i=index;i<=40;i++){
        arr.push_back(a[i]);
        dfs(i+1);
        arr.pop_back();
    }

}

int main()
{
    for(int i=1;i<=100;i++)//年份已固定,只用从后40位枚举
    {
        cin>>a_f[i];
    }
    for(int i=61;i<=100;i++){
        a[x++] = a_f[i];//只读取数组40位,从出现2023的3的后一个数读入数组
    }
    dfs(1);
    cout<<q.size()<<endl;//集合元素数量即符合的日期个数
    return 0;
}

        最开始是在数据结构课上学的搜索,不过当时只是涉及二叉树的遍历,对于排列问题遇到了也都是堆循环,直到准备蓝桥杯的时候才开始突击dfs,bfs这些,不过从结果来看,突击的结果好像不咋样...

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值