递归与递推

本文通过C++代码介绍了递归在解决指数型、排列型、组合型枚举问题的应用,包括AcWing92题的指数型枚举,AcWing94题的排列型枚举,AcWing93题的组合型枚举,以及AcWing1208题的翻硬币问题。递归在这些问题中用于生成所有可能的情况,并通过剪枝优化搜索过程,提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

递归与递推

AcWing 92. 递归实现指数型枚举

在这里插入图片描述

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N =20;
int n;
int st[N];
void dfs(int u)
{
    if(u>n)
    {
        for(int i=1;i<=n;i++)
            if(st[i]==1)
                cout<<i<<' ';
        cout<<endl;
        return;
    }
        st[u]=2;
        dfs(u+1);
        st[u]=0;

        st[u]=1;
        dfs(u+1);
        st[u]=0;

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

AcWing 94. 递归实现排列型枚举

在这里插入图片描述

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=10;
int path[N];//0表示还没放数 1~n表示放了哪个数
bool st[N];//true 表示用过 , false表示还没用过
int n;
void dfs(int u)
{
    if(u>n)//边界
    {
        for(int i=1;i<=n;i++)
            cout<<path[i]<<' ';
        cout<<endl;
        return;
    }
    //依次枚举每个分支,即当前每个位置可以填那些数
    for(int i=1;i<=n;i++)
    {     
        if(!st[i])
        {
            st[i]=true;
            path[u]=i;
            dfs(u+1);
            
            //path[u]=0; 恢复现场 因为下次会直接覆盖掉path[i]的值
            st[i]=false;//恢复现场
        }
        
    }
}
int main(int u)
{
    cin>>n;
    dfs(1);
    return 0;
}

AcWing 93. 递归实现组合型枚举

在这里插入图片描述

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=30;
int n,m;
int path[N];
int start;
void dfs(int u,int start)
{   
    if(u+n-start<m) //剪枝 n-1+n-start+1<m  n-1为当前选了几个数 n-start+1为还剩几个数可用 如果加起来小于要选的个数 即不成立
        return;
    if(u==m+1)
    {
        for(int i=1;i<=m;i++)
            cout<<path[i]<<' ';
        cout<<endl;
        return;
    }
    
    for(int i=start;i<=n;i++)
    {
        path[u]=i;
        dfs(u+1,i+1);
        //path[u]=0; 恢复现场
    }
}
int main()
{
    cin>>n>>m;
    dfs(1,1);
    return 0;
}

AcWing 1208. 翻硬币

在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=110;

int main()
{
    string s1;
    string s2;
    cin>>s1>>s2;
    int res=0;
    for(int i=0;i<s1.length()-1;i++)//枚举到长度-1,因为一定有解 最后一个的状态是由前一个确定的
    {
       if(s1[i]!=s2[i])
       {
        //   if(s1[i]=='*') s1[i]='o'; 其实不用翻转这一个 直接翻转下一个即可 依次往后看
        //   else s1[i]='*';

           if(s1[i+1]=='*') s1[i+1]='o';
           else s1[i+1]='*';
           res++;
       }
    }
    cout<<res<<endl;
    return 0;
}

AcWing 95.费解的开关

在这里插入图片描述
在这里插入图片描述
思路:
可以枚举第一行的灯,每一个灯对应两种状态(开/关),第一行五个灯一共有2的五次方也就是32种状态(第一个for循环)
为什么枚举第一行?因为下面的一行状态都是由上一行决定的,当第一行确定之后,我们再依次确定下面的每一行的状态,最后判断最后一行的灯,如果最后一行全部都是亮的,那么这一次操作就是正确的,记录操作次数并更新最小的操作数
在这里插入图片描述

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=6;//开大一点儿 因为字符数组以换行结尾
char g[N][N],back[N][N];
int dx[5]={0,0,-1,1,0};//dx,dy依次对应 右、左、上、下、自己(当前拨动的开关)
int dy[5]={1,-1,0,0,0};

int n;
void turn(int a,int b)
{
    for(int i=0;i<5;i++)
    {
        int x=a+dx[i];
        int y=b+dy[i];
        if(x<0 || y<0 || x>=5 || y>=5) //出界
            continue;
        g[x][y]^=1;//因为0和1的编码对应的二进制只有最后一位不一样,差1 0异或1就是1
    }       
}
int main()
{
    cin>>n;
    while(n--)
    {
        int res=10;//其实大于6步就不合格了 用来更新状态
        int step=0;
        for(int i=0;i<5;i++)
            for(int j=0;j<5;j++)
                cin>>g[i][j];
        
        for(int i=0;i<32;i++)
        {//枚举第一行的32种状态,每种状态都可能对应一种结果,从中找出来操作数最小的
            step=0;
            //拷贝一份,下一次枚举的时候还用原来的数组
            memcpy(back,g,sizeof(g));
            for(int j=0;j<5;j++)
            {//j代表第一行的五个灯 当前位置为'0'表示不操作,为'1'表示操作
                if(i>>j&1)//位运算操作看第j位是否为0  j从0开始
                {
                    step++;//记录操作步数
                    turn(0,j);//改变这个灯和相邻四个灯的状态
                }
            }
            for(int i=0;i<4;i++)//枚举前四行
            {
                for(int j=0;j<5;j++)//每行5个灯
                {
                    if(g[i][j]=='0')//如果这个灯是关的,就改变它下面的灯
                    {
                        step++;
                        turn(i+1,j);
                    }
                }
            }
            
            bool dark=false;//用来判断最后是否成功
            for(int i=0;i<5;i++)
                for(int j=0;j<5;j++)//遍历最后一行灯的状态
                    if(g[4][j]=='0')//如果有不亮的的灯说明这一组操作数不能使灯全部变亮
                    {
                        dark =true;
                        break;
                    }
                    
            if(!dark) res=min(res,step);//如果灯全亮 更新操作数最小值
            memcpy(g,back,sizeof back);//还原g数组
        }
        if(res>6) cout<<-1<<endl;
        else cout<<res<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值