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

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

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

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

代码实现
#include

using namespace std;

int main()
{
    int a=0,b=1;
    int N=0;
    cin>>N;
    while(N--)
    {
        cout<

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 
#include 
#include 
#include 
    
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<

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

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

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

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

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

代码实现
#include
#include
#include
#include
#include

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

typedef pair 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];
    //记录方案
    vectorres;
    //枚举所有方案
    for(int op=0;op<1<<16;op++)
    {
        vectortemp;//存的方案
        //先备份一下,为什么?因为这又不是最终方案,我们要把所有方案都试一遍,求最少的
        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<

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

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

代码实现
#include

using namespace std;
int main()
{
    string a,b;
    cin>>a>>b;
    int ans=0;
    for(int i=0;i

你可能感兴趣的:(蓝桥杯,算法,c++,数据结构)