模拟算法
模拟算法介绍:
模拟算法是一种基础的算法类型,它没有一个严格的定义,但可以被理解为直接根据问题的描述来实现算法。这种算法通常涉及到按照题目的要求,一步步地编写代码,模拟题目中描述的过程或操作。模拟算法通常用于解决一些基础的问题,有时被认为是比较简单的“水题”,但实际上,它们也可以非常复杂和具有挑战性。
- 说人话就是根据题目给出来的要求或过程通过代码的形式给它实现出来
模拟算法的特点:
模拟算法的主要特点是代码量大、操作多、思路复杂。由于代码量大,这类问题经常会出现难以查错的情况,尤其在考试中,一旦写错,可能会浪费大量时间。因此,尽管模拟算法可能看起来简单,但实际上需要非常仔细和谨慎地处理。
模拟算法的实现过程:
实现模拟算法的过程通常包括以下几个步骤:
理解题目:首先要完全理解题目的要求和目标。
建立模型:根据题目的要求,选择合适的数据结构和算法。
编写代码:根据建立的模型,编写代码来模拟题目描述的过程。
调试和优化:调试代码,确保它能正确地模拟所需的过程,并进行必要的优化。
模拟算法的建议:
在处理模拟算法时,以下建议可能会提高效率:
在开始编写代码之前,尽可能在草稿纸上写出要实现的流程。
将代码中的每个部分模块化,写成函数、结构体或类。
对于可能重复使用的概念,进行统一转换,以减少概念混淆。
分块调试代码,模块化的好处是可以方便地单独调试某一部分。
编写代码时要保持思路清晰,按照计划的步骤编写,而不是随意地写。
说人话就是:先把具体的步骤流程写出来,可以实现后再进行编写代码,不然很容易出错
一般来说模拟题型题目都会很长,需要仔细阅读和分析
替换所有问号:
题目分析:
题目的意识是:给出仅有小写英文字母和?的字符串 ,将?转换成小写字母 最后使得字符串不含重复的字符 ,保证除?外的字符不重复,不能修改非?字符
- ?只会影响它左右两边的字母
- 所以让?不等于左右两边的字母即可,从a遍历到b找?处理即可
- 需要注意的是:
- 当?在字符串开头时 只需要考虑右边字母即可
- 当?在字符串末尾时,只需要考虑左边的字母即可
- 所以我们可以创建 两个 char 类型数据 保存空格
- 如果没有左边或右边,也不会影响我们的判断
相应程序:
#include<iostream>
#include<string>
using namespace std;
string s;
int main(){cin>>s;//输入字符串for(int i=0;i<s.size();i++){if(s[i]=='?') {char c1=' ';char c2=' ';if(i-1>=0) c1=s[i-1];//找左边字母 if(i+1<s.size()) c2=s[i+1];//找右边字母 //找数for(char ch='a';ch<='z';ch++){if(c1!=ch&&c2!=ch){s[i]=ch;break;} } }} cout<<s<<endl;return 0;
}
生活大爆炸版石头剪刀布:
题目分析:
题目的意思是:有一个进阶版的剪刀石头布,由A和B进行比赛
首先 A和B都是有出拳规律的,会按照一定的规律进行出圈(可以使用数组保存)
出拳有5种情况:0剪刀 1石头 2布 3蜥蜴人 4 斯波克(只会输入数字)
根据它们的出拳关系可以用一个二维数组保存其对比的结果,方便后续的比较
二维数组数据:A赢 数据为1 B赢数据为-1 ,其他为0
对比时 如果值为1 A的得分+1 如果是-1 B的得分+1
需要输入的是
N局数 Na A的出拳周期 Nb B的出拳周期
A的出拳规律
B的出拳规律
需要注意的是出拳周期:出拳时需要特殊判断处理一下即可 使用% 或者判断都可以
相应程序:
#include<iostream>
using namespace std;
int main(){//用矩阵存储对比的结果 0 代表剪刀 1 代表石头 2 代表布 3 代表蜥蜴人 4 代表斯波克int a[5][5]={ //值 0代表平 1代表赢 -1代表输 {0,-1,1,1,-1},{1,0,-1,1,-1},{-1,1,0,-1,1},{-1,-1,1,0,1},{1,1,-1,-1,0},};//N共进行多少次拳 int N,N1,N2;cin>>N>>N1>>N2;//数组保存出拳周期int arr1[N1];int arr2[N2];for(int i=0;i<N1;i++){ //读取第一行数据 cin>>arr1[i];} for(int i=0;i<N2;i++){ //读取第二行数据 cin>>arr2[i];}//开始猜拳 int num1=0;//数组位置 int num2=0;//数组位置 int da1=0;//得分 int da2=0;//开始比赛for(int i=1;i<=N;i++){int p=arr1[num1];int q=arr2[num2];if(a[p][q]==1) {da1++;} else if(a[p][q]==-1){da2++;}if(num1==N1-1) num1=0;else num1++;if(num2==N2-1) num2=0;else num2++;} cout<<da1<<" "<<da2; return 0;
}
玩具谜题:
题目分析:
题目的意思是:现在有n个人围成一个圈,但人的方向有两种 朝里和朝外
现在要我们根据指令找到藏起来的眼镜:
- 输入人物时: 0代表朝里 1代表朝外 加上其名字
- 输入指令时: 0代表左边 1代表右边 s代表步长
- 首先需要创建string数组和int类型数组存储 人物的朝向和姓名 分别将数据输入
- 再创建两个int 类型变量存储 左右方向和步长 边输入边处理
- 首先以第一个读入的任务开始:由于是循环 我们需要确定 左右行走方向
根据上面的分析,我们可以按照它的要求写出相应的程序
相应程序:
#include<iostream>
using namespace std;
int main(){int n,m;cin>>n>>m;int a[n]; //0代表朝内 1朝外 string s[n];for(int i=0;i<n;i++){cin>>a[i]>>s[i];//输入数据 }//逆时针保存的数据//朝里左边 往小的//朝里右边 往大的//朝外左边 往大的//朝外右边 往小的//从第一个人开始处理int index=0;//下标 for(int i=1;i<=m;i++){int p,p1;cin>>p>>p1;//0代表左边 1代表右边if(a[index]==0&&p==1||a[index]==1&&p==0){index=(index+p1)%n;}else{int number=index-p1;if(number<0) index=n+number;else index=number;} } cout<<s[index];return 0;
}
日历制作:
题目分析:
题目的意思是:给出了2025年9月的日历,求m月的日历
- 已知的是下一个月的第一天会接到上一个月的末尾
- 所以我们可以使用%进行处理,但会有一点麻烦
暴力解法:
- 已知6月和9月份的日历,我们可以手动逆推和顺推出1-12月的日历
- 分别保存1-12月份的第一天的位置即可
- 推出来的结果存放到数组中 更具m的值输出哪一个月的值
- 这里需要注意的是 MON 占了3格 其他的由于有一个空格间隔,所以长度为4
- 当第一天不为Mon的时候 需要补齐空格
- 设置输出长度的函数为setw() #include<iomanip>
相应程序:
#include<iostream>
#include<iomanip>
using namespace std;//自己手动逆推或顺推出第一天的位置
int arr[13]={0,3,6,6,2,4,7,2,5,1,3,6,1,};//1-12月的第一天void show(int index,int data){ //输出if(index!=1){ //输出空格 for(int i=index-2;i>0;i--){cout<<" ";} cout<<" "; } for(int i=1;i<=data;i++){if(index==7){cout<<setw(4)<<i<<endl;index=1;}else if(index==1) {cout<<setw(3)<<i;index++;}else {cout<<setw(4)<<i;index++;}}
}
int main(){int m;cin>>m;//保存每个月的第一天的星期cout<<"MON TUE WED THU FRI SAT SUN"<<endl;switch(m){case 1:case 3:case 5:case 7:case 8:case 10:case 12:show(arr[m],31);break;case 4:case 6:case 9:case 11:show(arr[m],30);break;case 2:show(arr[m],28);break;}return 0;
}
图像压缩:
题目分析:
题目的意思是:我会输入一个十六进制表示的一个图像,一共有n行,每一行32个字符,一共16个16进制数,让我们处理的是:
- 求出这张图中的个数最多的16进制数,取前16个,个数最多的16进制数输出(如果个数相同,按照灰阶值从小到大排序)
- 将图像进行压缩:
- 将图像中的每个16进制数 P,转换成与16个选出的数据X的绝对值最小的那个数据
- 然后输出这个数据 :min abs(P,Xn) n=1-16;
- 对比的话可以直接循环判断 才16次比较
- 首先我们要存储16进制的个数,有要对比其10进制的值,还要统计个数
- 所以要创建一个16进制转10进制的函数进行处理
- 直接创建一个结构体,保存10进制的值,16进制的值和数据的个数
- 创建一个结构体数组,使用桶排序理念创建 0-255 的数组,值为这个范围直接仍进桶里
- 读取数据边进行转换存储
- 需要选取数量最多的16个16进制数,使用sort()函数对结构体数组进行排序
- 排序的规则为 如果个数相同 返回小的灰度值 否则 按照个数从大到小排序
- 图像压缩:直接与16个数据进行绝对值运算,找到最小的那个,输出对应的16进制数
相应程序:
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
struct Node{int data;//10进制string s;//灰阶 int number=0;//个数 }arr[260];//使用0-255这个部分 string ss[25];//存储输入进来的数据 //比较的仿函数
bool cmp(Node n1,Node n2){if(n1.number==n2.number)return n1.data<n2.data; return n1.number>n2.number;
}
//十六转10进制
int T_16_10(string s){int sum=0;int num=16;for(int i=0;i<=1;i++){ switch(s[i]){case 'A': sum+=10*num;break;case 'B': sum+=11*num;break;case 'C': sum+=12*num;break;case 'D': sum+=13*num;break;case 'E': sum+=14*num;break;case 'F': sum+=15*num;break;default: sum+=(s[i]-'0')*num; } num/=16; }return sum;
} int main(){int n;cin>>n;int data;for(int i=1;i<=n;i++){cin>>ss[i];//读取字符串for(int j=0;j<ss[i].size();j+=2){string str="";//读取16进制数str+=ss[i][j];str+=ss[i][j+1];data=T_16_10(str);arr[data].s=str;//将16进制保存 arr[data].data=data;//存入10进制 arr[data].number++;//个数+1 } }sort(arr,arr+256,cmp);//排序 for(int i=0;i<16;i++) cout<<arr[i].s;cout<<endl;//开始匹配for(int i=1;i<=n;i++){for(int j=0;j<ss[i].size();j+=2){string str="";str+=ss[i][j];str+=ss[i][j+1];data=T_16_10(str);int min_num=9000;//获取最小的绝对值int index=-1;//获取数据的位置for(int k=0;k<16;k++){ //与16个灰阶比较绝对值大小if(abs(data-arr[k].data)<min_num){min_num=abs(data-arr[k].data);index=k;}}switch(index){ //将位置转换为16进制表示case 15: cout<<"F";break;case 14: cout<<"E";break;case 13: cout<<"D";break;case 12: cout<<"C";break;case 11: cout<<"B";break;case 10: cout<<"A";break;default: cout<<index;break;}} cout<<endl;} return 0;
}
魔法少女小Scarlet
题目分析:
题目的意思是:给出一个n*n的方阵 值分别为1—n*n 它会根据指令旋转90度
- 现在给出一个指令 中心点x,y 和 z顺逆时针 2r+1的矩阵为
- z=0代表顺时针旋转 z=1代表逆时针旋转
- 输出经过多条命令旋转后的矩阵
- 首先需要创建一个数组存储矩阵
- 需要实现顺时针旋转90°的代码
- 需要实现逆时针旋转90°的代码
- 根据指令模拟即可
- 最后输出矩阵
相应程序:
#include<iostream>
#include<string>
using namespace std;
int arr[505][505];//存储方阵 从1开始使用
int n,m; //矩阵的边长,魔法的次数
int x,y,z,r;//魔法的参数void init(){ //初始化矩阵for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){arr[i][j]=(i-1)*n+j;}}
}
//逆时针旋转90度
void ni_90(){int num=2*r+1;//区域长度int t[505][505];//临时数组//获取旋转后的数据 for(int i=0;i<num;i++)for(int j=0;j<num;j++)t[i][j]=arr[x-r+j][y+r-i];//将数据复制给数组中 for(int i=0;i<num;i++)for(int j=0;j<num;j++)arr[x-r+i][y-r+j]=t[i][j]; }
//顺时针旋转90度
void shun_90(){int num=2*r+1;//区域长度int t[505][505];//临时数组//获取旋转后的数据 for(int i=0;i<num;i++)for(int j=0;j<num;j++)t[i][j]=arr[x+r-j][y-r+i];//将数据复制给数组中 for(int i=0;i<num;i++)for(int j=0;j<num;j++)arr[x-r+i][y-r+j]=t[i][j];
}
int main(){cin>>n>>m;init();while(m--){cin>>x>>y>>r>>z;z?ni_90():shun_90(); } for(int i=1;i<=n;i++){ //输出数据for(int j=1;j<=n;j++){cout<<arr[i][j]<<" ";}cout<<endl;}return 0;
}