C++算法之递归与递推(2)

二、递推(与递归相反,先求出子问题再去算出原问题)

1.AcWing 717.简单斐波那契
分析过程

先定义f(1)和f(2)作为边界,然后f(n)=f(n-1)+f(n-2),此题可以直接用滚动数组的雏形来算,用来节省空间

代码实现
#include<iostream>

using namespace std;

int main()
{
    int a=0,b=1;
    int N=0;
    cin>>N;
    while(N--)
    {
        cout<<a<<' ';
        int c=a+b;
        a=b;
        b=c;
    }
    return 0;
}

2.AcWing 95.费解的开关
分析过程

如果我们枚举第一行确定第一行的开关结果,那我们就可以确定第二行的开关情况,如果第一行第i个位置是关,则第二行第i个位置必须按一下。

①如何枚举第一行的操作

枚举第一行的意义是:不需要在意第一行的灯是灭是暗,只需把第一行的按法枚举一遍,也就是我们说的 “操作”,每个位置都有两种选择,按(用1表示)或者不按(用0表示),遍历这2^5=32种操作引发的情况,每一次再通过res = min(res, step);把最小步数存一下,就能找到最优解。

②turn(x,y)开关的这个操作实现

g[a][b]^=1;这行的代码采用位运算,‘0’的ASCALL码为48,对应的二进制位最右侧为0,与1做异或,对应的最右侧的0变成了1,同时ASCALL码也变成了49,49对应到char类型就是‘1’.同理可分析‘1’^1

③时间复杂度的计算

代码实现
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
    
using namespace std;    
const int N=6;  
char g[N][N],backup[N][N];
int dx[5]={-1,0,1,0,0},dy[5]={0,1,0,-1,0};
void turn(int x,int y)
{
    for(int i=0;i<5;i++)
    {
        int a=x+dx[i],b=y+dy[i];
        if(a<0||a>=5||b<0||b>=5) continue;//在边界外,直接忽略
        g[a][b]^=1;//0变成1的位运算 0的ASCII码是
    }
}
int main()    
{
    int T;
    cin>>T;
    while(T--)
    {
        for(int i=0;i<5;i++) cin>>g[i];
        
        int res=10;
        for(int op=0;op<32;op++)//0~2^5-1
        {
            memcpy(backup,g,sizeof(g));
            int step=0;
            for(int i=0;i<5;i++)
            {
                if(op>>i&1)
                {
                    step++;
                    turn(0,i);
                }
            }
            
            for(int i=0;i<4;i++)
            for(int j=0;j<5;j++)
            if(g[i][j]=='0')
            {
                step++;
                turn(i+1,j);//上面如果是没开的,那下一行就必须按
            }
            bool dark=false;
            for(int i=0;i<5;i++)
            if(g[4][i]=='0')//最后一行没办法由下一行该变了
            {
                dark=true;
                break;
            }
            
            if(!dark) res=min(res,step);
            memcpy(g,backup,sizeof(g));
        }
        if(res>6) res=-1;
        
        cout<<res<<endl;
    }
    
    return 0;    
}

3.AcWing 116.飞行员兄弟
分析过程

①16个开关,所有开关的状态数量是2^16,所有可以直接采用暴力的方法

②如果可以实现把手全开,证明此方案合法

③然后统计这个方案里面需要操作的把手数量

④在所有能按的开关数量里取一个最小值

代码实现
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>

//使用x,y的时候就表示first,second
#define x first
#define y second
using namespace std;

typedef pair<int,int> PII;

const int N =5;

char g[N][N],backup[N][N];

//映射函数
int get(int x,int y)
{
    return x*4+y;返回第x行第y列上的数是多少
}
void turn_one(int x,int y)
{
    if(g[x][y]=='+')g[x][y]='-';
    else g[x][y]='+';
}
void turn_all(int x,int y)
{
    for(int i=0;i<4;i++)
    {
        turn_one(x,i);
        turn_one(i,y);
    }
    //重复开关x,y
    turn_one(x,y);
}
int main()
{
    //读入数据
    for(int i=0;i<4;i++)
    for(int j=0;j<4;j++)
    cin>>g[i][j];
    //记录方案
    vector<PII>res;
    //枚举所有方案
    for(int op=0;op<1<<16;op++)
    {
        vector<PII>temp;//存的方案
        //先备份一下,为什么?因为这又不是最终方案,我们要把所有方案都试一遍,求最少的
        memcpy(backup,g,sizeof g);
        
        //枚举16个位置,进行操作
        for(int i=0;i<4;i++)
        {
            for(int j=0;j<4;j++)
            {
                如果当前位置是1的话--get的作用就是返回二进制数中那一位是第几位,从而判断是否为1
                if(op>>get(i,j)&1)
                {
                    temp.push_back({i,j});
                    //按一下开关
                    turn_all(i,j);//对i行j列进行操作
                }
                
            }
        }
        
        //判断所有灯泡是否全亮
        bool has_closed=false;
        for(int i=0;i<4;i++)
        {
            for(int j=0;j<4;j++)
            {
                if(g[i][j]=='+') has_closed=true;
            }
        }
        
        if(has_closed==false)
        {
            //如果方案为空或者他的操作数大于我们刚存好的新的方案,那么就修改它
            if(res.empty()||res.size()>temp.size()) res=temp;
        }
        //还原回来,供下一个方案操作
        memcpy(g,backup,sizeof g);
        
    }
    cout<<res.size()<<endl;
    //原题下标从1开始,所以下面加1了
    for(auto op:res) cout<<op.x+1<<" "<<op.y+1<<endl;
    return 0;
}

4.AcWing 1208. 翻硬币
分析过程

简单的枚举递推即可实现,一个一个对照,如果一致则不改变,不一样则改变与此同时下一个也进行改变!

代码实现
#include<iostream>

using namespace std;
int main()
{
    string a,b;
    cin>>a>>b;
    int ans=0;
    for(int i=0;i<a.length();i++)
    {
        if(b[i]!=a[i])
        {
            b[i]=a[i];
            if(b[i+1]=='*') b[i+1]='o';
            else b[i+1]='*';
            ans++;
        }
    }
    cout<<ans<<endl;
    return 0;
}

  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
算法是一种通过已知的前一项值来导出下一项值的数学算法。在计算机科学中,算法常用于解决循环或归问题,通过将较大的问题拆解成较小的子问题来求解。 第三章讲解了算法的实现方法和应用场景。在C语言中,算法可以通过循环结构来实现。比如,斐波那契数列是一个经典的算法问题。通过定义前两项的初值为1,然后通过循环不断计算出下一项的值,最终得到一个斐波那契数列。 下面是一段使用算法计算斐波那契数列的C语言代码: ```c #include <stdio.h> int fibonacci(int n) { int a = 1; int b = 1; int c; if (n < 1) { return -1; // 输入不合法,返回错误代码 } else if (n < 3) { return 1; // 第一项和第二项的值为1 } for (int i = 3; i <= n; i++) { c = a + b; a = b; b = c; } return b; } int main() { int n = 10; int result = fibonacci(n); printf("第%d项的值为:%d\n", n, result); return 0; } ``` 通过循环计算,上述代码可以输出斐波那契数列的第10项的值为55。 算法在实际应用中还有很多其他的用途。比如,可以用算法来解决排列组合问题、动态规划问题等。在编程中,算法的优势在于其简单高效的计算方法,能够较快地求解问题。然而,有时候算法需要注意边界条件和初始值的设定,以避免出现错误的结果。 总结起来,算法是一种通过已知的前一项值来导出下一项值的数学算法。在C语言中,算法可以通过循环结构来实现。算法在计算机科学中有着广泛的应用,能够简化问题求解的过程,提高计算效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值