第一讲 递归和递推

目录

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

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

AcWing 717. 简单斐波那契

AcWing 95. 费解的开关


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

从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。

输入格式

输入一个整数 n。

输出格式

每行输出一种方案。

同一行内的数必须升序排列,相邻两个数用恰好 1 个空格隔开。

对于没有选任何数的方案,输出空行。

本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。

数据范围

1≤n≤15

输入样例:

3

输出样例:


3
2
2 3
1
1 3
1 2
1 2 3

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

#include

using namespace std;

int n;

void dfs(int u,int state){
    //如果枚举到第n个数
    if(u==n){
        //就输出前面枚举的结果,利用state来确定第u个数是否被选过
        for(int i = 0;i>i & 1){
                //输出的数是从1开始枚举,而我们的计算是从0开始
                cout <>n;
    //数从0开始枚举,第二个参数为0,说明初始化所有的比特位为0
    dfs(0,0);
   
    return 0;
}

state>>i & 1 先将第i位移到第1位然后判断它是0还是1

state|1<

94. 递归实现排列型枚举

把 1∼n 这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。

输入格式

一个整数 n。

输出格式

按照从小到大的顺序输出所有方案,每行 1 个。 

首先,同一行相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。

数据范围

1≤n≤9

输入样例:

3

输出样例:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
#include
#include

using namespace std;

int n;

//相当于c语言里面的一维数组
vector path;

void dfs(int u,int state){
    if(u==n){
        //和java里面的增强for循环一样。到了南墙,输出所有的数。
        for(auto i:path)cout<>i&1)){
            //如果没有使用过就入坑
            path.push_back(i+1);
            //搜索下一个数,然后把state的第i位标记为1,表示第i个数已经被使用过了
            dfs(u+1,state|1<>n;
    //param1:从0开始搜索到n-1
    //param2:二进制数表示哪些数被用过了,这里表示第一个数没有被用过开始搜索。
    dfs(0,0);
    return 0;
}

717. 简单斐波那契

以下数列 0 1 1 2 3 5 8 13 21 ... 被称为斐波纳契数列。 

这个数列从第 3 项开始,每一项都等于前两项之和。

输入一个整数 N,请你输出这个序列的前 N 项。

输入格式

一个整数 N。

输出格式

在一行中输出斐波那契数列的前 N 项,数字之间用空格隔开。

数据范围

0

输入样例:

5

输出样例:

0 1 1 2 3

递推做法

#include

using namespace std;

int main(){
    int n;
    //数据范围
    int a[46];
    cin>>n;
    //初始条件
    a[0] = 0;
    a[1] = 1;
    //递推
    for(int i=2;i

优化:滚动数组的雏形,节省空间

#include

using namespace std;

int main(){
    int n;
    cin>>n;
    //初始条件
    int a = 0,b = 1;
    //递推
    for(int i=0;i

AcWing 95. 费解的开关

你玩过“拉灯”游戏吗?

25 盏灯排成一个 5×5 的方形。

每一个灯都有一个开关,游戏者可以改变它的状态。

每一步,游戏者可以改变某一个灯的状态。

游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。

我们用数字 1 表示一盏开着的灯,用数字 0 表示关着的灯。

下面这种状态

10111
01101
10111
10000
11011

在改变了最左上角的灯的状态后将变成:

01111
11101
10111
10000
11011

再改变它正中间的灯后状态将变成:

01111
11001
11001
10100
11011

给定一些游戏的初始状态,编写程序判断游戏者是否可能在 6 步以内使所有的灯都变亮。

输入格式

第一行输入正整数 n,代表数据中共有 n 个待解决的游戏初始状态。

以下若干行数据分为 n 组,每组数据有 5 行,每行 5 个字符。

每组数据描述了一个游戏的初始状态。

各组数据间用一个空行分隔。

输出格式

一共输出 n 行数据,每行有一个小于等于 6 的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。

对于某一个游戏初始状态,若 6 步以内无法使所有灯变亮,则输出 −1。

数据范围

0

输入样例:

3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111

输出样例:

3
2
-1
#include
#include

using namespace std;
//先把大体框架搭出来,然后再实现细节的函数
//取名最好见名知意,debug的时间往往要多于写代码

int const INF = 1000000;

char g[10][10];

//这两个一维数组表示本身和周围共5个位置
int dx[5] = {0,-1,0,1,0} , dy[5] = {0,0,1,0,-1};

//功能:将某个下标和周围的数取反
void turn(int x,int y){
    int a,b;
    //遍历周围的五个方向,分别取反
    for(int i = 0;i<5;i++){
        //a和b为偏移后位置的下标
        a = x + dx[i];
        b = y + dy[i];
        //如果偏移后的位置没有越界就取反
        if(a>=0&&a<5&&b>=0&&b<5){
            g[a][b] ^= 1;
        }
    }
}

int work(){
    //这个数组用来做备份,来存储g的原来状态
    char backup[10][10];
    //等于一个较大的数
    int ans = INF;
    //将g复制到backup里,cstring头文件中
    memcpy(backup, g, sizeof g);
    //遍历第一行的全部32种情况,对第一行进行的操作,决定了后面几行的处理情况。
    //只有将第一行按压灯泡的所有可能情况都取到,才能补充不漏的找到最少的操作数。
    for(int k = 0;k<1<<5;k++){
        //开始一种新的情况
        int res = 0;
        //如果有1就按压,这里的1不代表灯泡的状态,而是代表是否按压灯泡
        //第i位是否是1,如果是1就进行按压,操作数增加
        for(int i = 0;i<5;i++){
            if(k>>i&1){
                res++;
                turn(0, i);
            }
        }
        //上面是对第一行进行操作后的初始状态,接下来除最后一行,每一行都跟着进行处理
        //如果某个位置是0,就把这个位置下方的数按压,让这个位置的数变成1
        for(int i = 0;i<4;i++){
            for(int j = 0;j<5;j++){
                if(g[i][j]=='0'){
                    res++;
                    turn(i+1, j);
                }
            }
        }
        
        //利用最后一行判断是否处理成功,也就是所有灯泡都是亮的
        bool is_successful = true;
        for(int i = 0;i<5;i++){
            if(g[4][i]!='1'){
                is_successful = false;
                break;
            }
        }
        
        //获得32种情况中,满足条件的最小的数
        if(is_successful) ans = min(ans,res);
        
        //一种情况计算完毕,将g重新复原,进行下一个状态的计算
        memcpy(g, backup, sizeof g);
    }
    
    //题目要求,如果操作数大于6输出-1
    if(ans>6) ans = -1;
    return ans;
}

int main(){
    
    int T;
    cin>>T;
    while(T--){
        //因为是二维的字符数组,所以可以一行行输入
        for(int i = 0;i<5;i++) cin>>g[i];
        
        cout<

你可能感兴趣的:(算法,java,jvm,数据结构)