【DFS】学习

我看的b站上的这个视频,感觉讲得还不错~https://www.bilibili.com/video/BV1S24y1p7iH/?spm_id_from=333.880.my_history.page.click

递归实现指数型枚举

题目链接:92. 递归实现指数型枚举 - AcWing题库

从 1∼n这 n个整数中 随机选取任意多个,输出所有可能的选择方案。
输入格式
输入一个整数 n。
输出格式
每行输出一种方案。
同一行内的数必须升序排列,相邻两个数用恰好 1
个空格隔开。
对于没有选任何数的方案,输出空行。
本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。
数据范围
1≤n≤15
输入样例:
3
输出样例:
3
2
2 3
1
1 3
1 2
1 2 3
【DFS】学习_第1张图片

(本来尝试用画图工具的,但我还是太拉了,没有ipad好伤

#include 
#include 
using namespace std;
const int N =20;
int n;
int st[N];//记录状态 0表示还没考虑,1表示选,2表示不选 
void dfs(int x)
{
    if(x>n)//递归退出条件
    {
        for(int i=0;i<=n;i++)
        {
            if(st[i]==1)
            {
                cout<>n; 
    dfs(1);
    return 0;
}

递归实现排列型枚举

题目链接:P1706 全排列问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述
按照字典序输出自然数 1 到 n 所有不重复的排列,即 n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
输入格式
一个整数 n。
输出格式
由 1∼n 组成的所有不重复的数字序列,每行一个序列。
每个数字保留
5 个场宽。
输入输出样例
输入 #1复制
3
输出 #1复制
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

首先分析一下这道题叭


【DFS】学习_第2张图片

1.依次枚举每个位置应该放哪个数

#include 
#include 
using namespace std;
const int N=10;
int n;
int arr[N];
bool st[N];
void dfs(int x)//枚举位置 
{
    if(x>n)
    {
        for(int i=1;i<=n;i++)
        {
            printf("%5d",arr[i]);
        }
        printf("\n");
        return ;
    }
    for(int i=1;i<=n;i++)//遍历数字1,2,3 
    {
        if(!st[i])//当前数没有被用过    
        {
            st[i]=true;
            arr[x]=i;//存答案
            dfs(x+1);

            arr[x]=0;
            st[i]=false; 
        }
    }     
}

int main()
{
    cin>>n;
    dfs(1);
    return 0;
} 

2.依次枚举每个数应该放哪个位置

#include 
#include 
using namespace std;
const int N=10;
int n;
int st[N];

void dfs(int x)//枚举数 
{
    if(x>n)
    {
        for(int i=1;i<=n;i++)
        {
            printf("%5d",st[i]);
        }
        printf("\n");
        return ;
    }
    for(int i=1;i<=n;i++)//遍历位置1,2,3 
    {
        if(st[i]==0)//当前位置没有被填上    
        {
            st[i]=x;//填上 
            dfs(x+1);
            st[i]=0; 
        }
    }     
}

int main()
{
    cin>>n;
    dfs(1);
    return 0;
} 

这个代码输出不是按字典序,在洛谷上不能AC(苦恼ing,求大佬指点!!!

递归实现组合型枚举

题目链接:P1157 组合的输出 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这题说人话,就是从n个数中取出r个,就是数学中的组合,注意数字不要求顺序。

简单画个示意图:

【DFS】学习_第3张图片
#include 
#include 
using namespace std;
const int N =20;
int n,r;
bool st[N];
int arr[N];

void dfs(int x,int start)//遍历位置 ,start代表应该从这开始遍历数 
{
    if(x>r)
    {
        for(int i=1;i<=r;i++)
        {
            printf("%3d",arr[i]);
        }
        cout<>n>>r;
    dfs(1,1);
    return 0;
}

简单分析一下这里为什么要传入两个参数

【DFS】学习_第4张图片
【DFS】学习_第5张图片

看运行结果明显错误的在后面出现了比他大的元素(如5,4,3),这就导致了组合出现多个。因此要额外传入一个start的限制,使其遍历后面的数字。(纯菜鸟,初学,求指点)


补充一下剪枝:

还是这个图,这里剪枝的原理是能选的数已经不够填充位置了,

【DFS】学习_第6张图片

选数

题目链接:P1036 [NOIP2002 普及组] 选数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这种不要求顺序的,求组合数都可以这样遍历位置,dfs传入两个参数记录位置和下一位遍历数

要求顺序的排列数问题,就需要加一个if语句判断是否使用过,同样的遍历位置进行递归

#include 
#include 
using namespace std;
const int N =30;
int n,k;
int  arr[N];//存答案
int number[N];//存数组 
int cnt=0;
bool isprime(int x)
{
    for(int i=2;i<=x/i;i++)
    {
        if(x%i==0)
        {
            return false;
        }
    }
    return true;
}
void dfs(int x,int start)//遍历位置 ,start代表应该从这开始遍历数 
{
    if(x>k)//要选k个数 
    {
        int sum=0;
        for(int i=1;i<=k;i++)
        {
            sum+=arr[i];
        } 
        if(isprime(sum))    cnt++;
        return ;
    }
    for(int i=start;i<=n;i++)//遍历数字 
    {
        arr[x]=number[i];
        dfs(x+1,i+1);
        arr[x]=0; 
    }
}
int main()
{
    cin>>n>>k; 
    for(int i=1;i<=n;i++)
    {
        cin>>number[i];//输入数据 
    } 
    dfs(1,1);
    cout<

这里学到了一个小细节 ,判断素数的函数里,一般是写成i*i<=x,这里为了防止爆数据写成i<=x/i

for(int i=2;i<=x/i;i++)

了解一下剪枝叭

就是图上的这种情况,比如3,19这条线没有数可选了,但位置还没有填满这种分枝剪掉就可以啦;

也就是再dfs里加上一个判断,删去 已选择数+可选择数

对于选数这道题,代码实现一下看看:

【DFS】学习_第7张图片
    if((x-1)+n-start+1
#include 
#include 
using namespace std;
const int N =30;
int n,k;
int  arr[N];//存答案
int number[N];//存数组 
int cnt=0;
bool isprime(int x)
{
    for(int i=2;i<=x/i;i++)
    {
        if(x%i==0)
        {
            return false;
        }
    }
    return true;
}
void dfs(int x,int start)//遍历位置 ,start代表应该从这开始遍历数 
{
    if((x-1)+n-start+1k)//要选k个数 
    {
        int sum=0;
        for(int i=1;i<=k;i++)
        {
            sum+=arr[i];
        } 
        if(isprime(sum))    cnt++;
        return ;
    }
    for(int i=start;i<=n;i++)//遍历数字 
    {
        arr[x]=number[i];
        dfs(x+1,i+1);
        arr[x]=0; 
    }
}
int main()
{
    cin>>n>>k; 
    for(int i=1;i<=n;i++)
    {
        cin>>number[i];//输入数据 
    } 
    dfs(1,1);
    cout<

剪枝大概就是这样,比想象的要简单。同时,剪枝可以缩短很多时间哦~

烤鸡问题

题目链接:P2089 烤鸡 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

简单分析一下题目,10 种配料,每个配料1~3克,美味程度就在10~30这个区间

10个配料,每个调料有3种选择,选择数就有3的10次方个,配料的选择之间没有关系,也就是指数型枚举。

依次枚举每个调料放几克

#include 
#include 
using namespace std;

int n;
int res=0;//存方案数
int arr[11];//存临时方案
int mem[10001][11]; 
void dfs(int x,int sum)
{
    if(sum>n) return;//剪枝 
    
    if(x>10)
    {
        if(sum==n)
        {
            res++;
            for(int i=1;i<=10;i++)
            {
                mem[res][i]=arr[i];
            }
        }
        return ;
    } 
    
    for(int i=1;i<=3;i++)
    {
        arr[x]=i;
        dfs(x+1,sum+i);
        arr[x]=0;
    }
}

int main()
{
    cin>>n;
    dfs(1,0);
    cout<

火星人

题目链接:P1088 [NOIP2004 普及组] 火星人 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include 
#include 
using namespace std;
const int n=10010;
int N,M; //手指数目和要加上的小整数M<=100 
int mars[n];//手指排列顺序 
int arr[n];//记录方案 
bool st[n];//记录状态 
int res=0;
int cnt;
int flag=0; 
void dfs(int x)//列举位置 
{
    if(flag) return ;
    if(x>N)
    {
        res++;//完成了一次全排列
        if(res==M+1)
        {
            flag=1;//只要找到了答案下一次就不用递归了 
            for(int i=1;i<=N;i++)
            {
                cout<>N>>M;
    for(int i=1;i<=N;i++)
    {
        cin>>mars[i];
    }
    dfs(1);//直接从火星人目前手指开始枚举,枚举M次就可以了 
    return 0;
}

这题问了佬,知道了一种next_permutation库函数的方法,查了一下是c++的一个全排列函数

C++ STL全排列 next_permutation 用法 - 知乎 (zhihu.com)

【DFS】学习_第8张图片

火柴棒等式

题目链接:P1149 [NOIP2008 提高组] 火柴棒等式 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include
#include
using namespace std;
int count[10010]={6,2,5,5,4,5,6,3,7,6};
int n;
int cnt=0;
int arr[1000];

void dfs(int x,int sum)
{
    if(sum>n) return;
    if(x>3)
    {
        if(sum==n&&arr[1]+arr[2]==arr[3]) cnt++;
        return;
    } 
    for(int i=0;i<=1000;i++)//遍历数字 
    {
        arr[x]=i;
        dfs(x+1,sum+count[i]);
        arr[x]=0;    
    }
    
}
int main()
{
    cin>>n;
    n-=4;//减去加号和等号的火柴数 
    for(int i=10;i<=1000;i++)
    {
        count[i]=count[i%10]+count[i/10];
     } 
    dfs(1,0);
    cout<

PERKET

题目链接:P2036 [COCI2008-2009#2] PERKET - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

补一个求绝对值:(15条消息) C\C++ 中的绝对值函数:abs()、cabs()、fabs()、labs()_c++绝对值函数_YogLn的博客-CSDN博客

#include
using namespace std;
const int N=15;
int n;//种类数 
int s[N];
int b[N];
int res=1e9;
int st[N];
void dfs(int x)
{
    if(x>n)
    {
        bool tl=false;
        int sum1=1;
        int sum2=0;
        for(int i=1;i<=n;i++)
        {
            if(st[i]==1)
            {
                tl=true;
                sum1*=s[i];
                sum2+=b[i];
            }
        }
        if(tl) res=min(res,abs(sum1-sum2));
        return ;
    }
    
    st[x]=1;
    dfs(x+1);
    st[x]=0;
    
    st[x]=2;
    dfs(x+1);
    st[x]=0;
    
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&s[i],&b[i]);
    }
    dfs(1);
    cout<

奇怪的电梯

题目链接:P1135 奇怪的电梯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include
using namespace std;

const int N=210;
int n,a,b;//n总楼层,从a楼到b楼 
int k[N]; 

int res=1e9;
bool st[N];//存每层楼走没走过 

void dfs(int x,int cnt)//当前按了cnt次按钮 
{
    //剪枝 
    if(cnt>=res) return; 
    if(x<0||x>n) return ;
    if(x==b)
    {
        res=min(res,cnt);
        return;
    }
    st[x]=true; 
    if(x+k[x]<=n&&!st[x+k[x]])//上
    {
        st[x+k[x]]=true;
        dfs(x+k[x],cnt+1);
        st[x+k[x]]=false;
    }
    if(x>k[x]&&!st[x-k[x]])//下 
    {
        st[x-k[x]]=true;
        dfs(x-k[x],cnt+1);
        st[x-k[x]]=false;
    } 

}
int main()
{
    cin>>n>>a>>b;
    for(int i=1;i<=n;i++)
    {
        cin>>k[i];
    }
    dfs(a,0);
    if(res==1e9)
    {
        cout<<"-1"<

每层楼最多走一次的方案一定比每层楼走多次的方案要好

【DFS】学习_第9张图片

被卡掉了,暴力搜索过不了,应该是要用bfs。

你可能感兴趣的:(算法,学习)