算法笔记 - 递归

第九章 递归

9.1 斐波那契数列 见动态规划专题|| 汉诺塔问题

9.2 波兰式求值

这里写图片描述
dfs() 表示读取一个数字或者运算符并进行相应的atof转换或者递归运算
逆波兰式不需要递归只需要维护一个栈,读到数字就存入(全局)栈中,读到运算符就将栈顶两个元素进行运算,结果存入栈顶

#include<cstdio>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<set>
#include<vector>
#define MAX 10
#define  num 32767
#define INF 0x7f7f7f
#define eps 1e-5
using namespace std;
double dfs()
{
    string s;//读入一个数字或者运算符
    cin>>s;
    if(s[0]=='+')//对运算符进行递归
        return dfs()+dfs();
    else if(s[0]=='-')
        return dfs()-dfs();
    else if(s[0]=='*')
        return dfs()*dfs();
    else if(s[0]=='/')
        return dfs()/dfs();
    else//对数字进行转换
        return atof(s.data());
}
int main()
{
   //freopen("input.txt","r",stdin);
    printf("%f\n",dfs());
   return 0;
}

小技巧: string类型转换成char *

//string ->char * 需要调用函数
const char *c = s.data();//或者 char *c = (char *)s.data();
const char *c = s.c_str();//或者 char *c = (char *)s.c_str();
//char * -> string 直接复赋值
string s = c;

9.3 递归解决排列组合问题

9.3.1 从起点开始递增的排列组合(可重复或者不可重复,且与顺序无关)
可重复 与顺序无关

注意
1 枚举所有起点(main中的for)
2 枚举所有后继(dfs中的for)
3 dfs(出口参数 , 条件参数1 , 条件参数2…)

2749 分解因数(乘法分解)
这里写图片描述
dfs(int 当前积, int 上一个因子) 表示当积为dangqianji 且上一个因子是shangyigeyinzi的情况下能否完成分解

#include<cstdio>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#define MAX 53
#define  num 32767
#define INF 0x7f7f7f
#define eps 1e-5
using namespace std;
int n,a,ans;
void dfs(int k,int pre)//限定条件:递增 由参数pre实现(k用来判递归出口)
{
    if(k==a)
    {
        ans++;
        return ;
    }
    if(k>a) return ;
    for( int i=pre;i<=a;i++)//如果是不重复递增则i=pre+1
    {
        if(k*i<=a)
            dfs(k*i,i);
        else break;
    }
}
int main()
{
   //freopen("input.txt","r",stdin);
    cin>>n;
    for(int i=0;i<n;i++)
    {
        ans=0;
        cin>>a;
        for(int i=2;i<=a;i++) dfs(i,i);//枚举所有起点
        cout<<ans<<endl;
    }
   return 0;
}

1664 放苹果(加法分解) 可以看做是和确定的递增排列,且限定了加的次数(上一题因子分解没限定乘的次数,这里递归函数的参数要多加一个)
这里写图片描述
dfs(int 当前第几个盘 , int 总苹果 ,int 上一个盘放的苹果数) 表示该情况继续分解

#include<cstdio>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<set>
#include<vector>
#define MAX 10
#define  num 32767
#define INF 0x7f7f7f
#define eps 1e-5
using namespace std;
int m,n,k,t;
void dfs(int dangqianpan , int zongpingguo,int 上一个盘放的苹果数 )//这里多加了一个限定条件(加的次数也就是盘子总数)
{
        if(zongpingguo==m&&dangqianpan<n)
        {
            k++;
            return ;
        }
        if(zongpingguo >m||dangqianpan>n) return;

        for(int i=上一个盘放的苹果数;i<=m;i++)
            dfs(dangqianpan+1,zongpingguo+i,i);
}
int main()
{
   //freopen("input.txt","r",stdin);
   cin>>t;
   for(int i=0;i<t;i++)
   {
       cin>>m>>n;
       k=0;
       for(int i=1;i<=m;i++)//遍历可能的起点
           dfs(0,i,i);
        cout<<k<<endl;
   }
   return 0;
}

9.3.2 全排列问题(把1~n按照某种顺序不重复地摆放)
不重复 与顺序有关

注意
1 dfs(递归出口参数) 一般为全排列的总位数n

求n个数的全排列个数 || 求n个数的第k个全排列 可以看作不在同一行同一列的皇后
dfs(int 当前第几行) 表示在当前行放该行的数

扫描二维码关注公众号,回复: 1897109 查看本文章
//定义 vector<int> ans,temp(临时排列和最终排列);  bool visit[MAX];(某个数是否已经加入本次排列)
 void dfs(int index)
 {
     if(index==n+1)//注意是n+1,因为dfs(1)从1开始
     {
        //计数 或者 按照规则检查temp更新ans(如八皇后)
         return;
     }

     for(int i=1;i<=n;i++)//9.3.1 中因为是递增排列所以不需要一个标记数组visit
     {//这里在全排列中需要从头开始找没用过的数字
         if(!visit[i])
         {
             temp.push_back(i);
             visit[i]=1;
             dfs(index+1);
             visit[i]=0;//全排列问题需要还原状态
         }
     }
 }

2754 八皇后问题
这里写图片描述
dfs(int 当前第几行) 表示 在当前行放该行的数

#include <iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<map>
#include <math.h>
#include<algorithm>
#include <string.h>
#define eps 1e-10
#define ll long long
#define da    0x3f3f3f3f
#define xiao -0x3f3f3f3f

using namespace std;
int b;
vector<int > ans,temp;
bool visit[9]={false};
int num;
void dfs(int index)
{
    if(cnt==8)//从0开始所以是n
    {
        num++;//代表找到了第num个全排列
        if(num==b)
            ans=temp;
        return ;
    }
    if(index>8) return ;

    for(int i=0;i<8;i++)
    {
        if(!visit[i])
        {
            //遍历之前的皇后(八皇后问题的高校写法是将检查从出口移动到for中)
            //所谓回溯也就是将出口处的判断移动到后面的程序中
            bool flag=1;
            //八皇后只需要检查之前的皇后是否在k=+-1斜线上,因为不在同一行和同一列是必然的
            //index为当前行号,外层for是当前列号,内层for中是之前的行号,temp[]为之前的列号
            for(int j=0;j<index;j++)
                if(abs(index-j)==abs(i-temp[j])) flag=0;//abs保证k=+-1
            if(flag)
            {
                visit[i]=1;
                temp.push_back(i);
                dfs(cnt+1);
                temp.pop_back();
                visit[i]=0;
            }
        }
    }
}
 int main()
{
    //freopen("input.txt","r",stdin);
   int n;
   cin>>n;
   for(int i=0;i<n;i++)
   {
       ans.clear();
       temp.clear();
       memset(visit,0,sizeof(visit));
       num=0;
       cin>>b;
       dfs(0);
       for(int i=0;i<8;i++)
           cout<<ans[i]+1;
       cout<<endl;
   }
    return 0;
}

1321 棋盘问题 全排列计数(相当于在八皇后问题中去掉斜线条件,增加了一个可摆放位置条件和一个总棋子个数条件(皇后中棋子个数一定等于行数,这里可能小于行数因而递归出口))
这里写图片描述
dfs(int 当前行 , int 当前第几个数)表示在当前行放第几个数 与皇后相比多了一个条件参数,因为皇后问题每一行必定放一个数,所以行数等于棋子个数,也就不需要额外判断棋子数,这里棋盘若在当前行没找到符合条件的位置,仍然需要递归到下一行,所以需要额外参数标识

DFS(index+1,dijigeshu+1);//该行(第index行)找到了能放的位置并放入,到下一行
DFS(index+1,dijigeshu); //该行没有符合条件的位置,直接到下一行

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char a[10][10];     //记录棋盘位置
bool book[10];        //记录一列是否已经放过棋子
int n,k;
int total,m;    //total 是放棋子的方案数 ,m是已放入棋盘的棋子数目

void DFS(int index,int dijigeshu)
{
    if(dijigeshu==k)
    {
        total++;
        return ;
    }
    if(index>k)    //边界
        return ;
    for(int j=0; j<n; j++)
    //判断条件,与八皇后相比这里多了判断是否为棋盘区的条件
        if(book[j]==0 && a[index][j]=='#')
        {
            book[j]=1;
            DFS(index+1,dijigeshu+1);//该行(第index行)找到了能放的位置并放入,到下一行
            book[j]=0;
        }
    DFS(index+1,dijigeshu);  //与八皇后的区别,该行没有符合条件的位置,直接到下一
}

int main()
{
    int i,j;
//freopen("input.txt","r",stdin);
    while(scanf("%d%d",&n,&k)&&n!=-1&&k!=-1) //限制条件
    {
        total=0;
        m=0;
        for(i=0; i<n; i++)
            scanf("%s",&a[i]);
        memset(book,0,sizeof(book));
        DFS(0,0);
        printf("%d\n",total);
    }
    return 0;
}

9.4 草丛问题
2816 红与黑
这里写图片描述
dfs(int x ,int y) 表示访问点(x,y)

#include<cstdio>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<set>
#include<vector>
#define MAX 21
#define  num 32767
#define INF 0x7f7f7f
#define eps 1e-5
using namespace std;
int w,h,qidianx,qidiany;
int X[]={-1,1,0,0};
int Y[]={0,0,-1,1};
char mp[MAX][MAX];
bool visit[MAX][MAX];
bool check(int x ,int y)
{
    if(x<0||x>=h||y<0||y>=w) return 0;
    if( visit[x][y]||mp[x][y]=='#') return 0;
    return 1;
}
void dfs(int x,int y)
{
    //访问
    visit[x][y]=1;
    //四个方向遍历
    for(int i=0;i<4;i++)
    {
        int newx = x+X[i];
        int newy = y+Y[i];
        if(check(newx,newy))  dfs(newx,newy);
    }
    return ;
}

int main()
{
   //freopen("input.txt","r",stdin);
   while(cin>>w>>h&&w!=0&&h!=0)
   {
       memset(visit,0,sizeof(visit));
       for(int i=0;i<h;i++)
       {
           for(int j=0;j<w;j++)
           {
               cin>>mp[i][j];
               if(mp[i][j]=='@')
               {
                   qidianx=i;
                   qidiany=j;
               }
           }
       }
       dfs(qidianx,qidiany);
       int ans =0;
       //只有一个草丛,所有访问过的都是组成草丛的块
       for(int i=0;i<h;i++)
           for(int j=0;j<w;j++)
               if(visit[i][j]) ans++;
       cout<<ans<<endl;
   return 0;
}

猜你喜欢

转载自blog.csdn.net/zongza/article/details/80929038