二、递推(与递归相反,先求出子问题再去算出原问题)
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;
}