C++算法:模拟
前言
内容纯属原创,如有雷同,纯属巧合!
如转载请注明出处!
参考题目
洛谷
https://www.luogu.com.cn/problem/P1046
https://www.luogu.com.cn/problem/P1421
https://www.luogu.com.cn/problem/P1138
一、模拟所涉及的内容
模拟这个算法其实非常难,主要是逻辑上的麻烦,但正常刷题时我们都不把模拟的逻辑思维理清就直接做,如果这题没有太水的话,是非常容易错的。
模拟可以与任何算法同时出现,比如模拟与动归dp、模拟与搜索之类的
所以说模拟有很多等级的题,我前面写的参考题目都是一些入门的水题而已
二、模拟的逻辑思维
模拟,他的逻辑思维和“模拟”二字的本意一样
我们就拿https://www.luogu.com.cn/problem/P1046
这道题做例子,这道题大家一看就肯定能做出来吧?
无非就是伸手的高度+凳子的高度这个高度及以下的苹果嘛
但是,这道题的算法标签里有“模拟”这个表情,那我们就用模拟的思维来想
总共10个苹果,凳子高30,这些都是常数
不定数是苹果所在的高度、陶陶的高度
我们把第i个苹果的高度记做ai,陶陶的高度记做h
其实刚刚那一句话就用了模拟这个算法
“我们把第i个苹果的高度记做ai,陶陶的高度记做h”
第一小句,我们假设(“假设”这两个字划重点)第i个苹果高度记做ai
好的,那么我们苹果的高度已经被我们模拟了
有人可能不信,就这么简单?
没办法,我们已经把10个苹果的高度都模拟了一遍了,我们第一句话所需要所描述的已经被我们模拟了
那么我们来看第二小句
我们假设陶陶个高度是h,那么陶陶的总高度就是h+30,我们记做h1
这又是一次模拟
也就是说,模拟这个算法,我们把题目中所说的不定量(注意是题目中所说的!)的变量名写出来的那一刻起,我们在大脑里就已经模拟出了这个变量的大小
那么我们来具体实践下
二、模拟的具体实践
其实模拟没有什么实践方法,刚刚已经说了,就是起变量名的过程而已
模拟这个算法最重要的一点,就是逻辑的问题
你把逻辑想明白了,模拟这个算法也就无师自通了
要想做到融会贯通,那么还有一步
没错,就是构建题目中出现的不定量以外的数
比如陶陶摘苹果,题目只给我们了板凳和陶陶的高度,但是陶陶摘苹果时的实际高度是陶陶的高度+板凳的高度
那么这又是题目以外的不定量了
所以这一步,就是学会结合题目中的常数、公式、不定量以及一些思维逻辑来写出代码
首先我们构建苹果的高度,我们这里采用的是数组,你想使变量也可以,因为就只有10个苹果
那么我们列一下这道题的大纲
1.读入
2.计算(看看哪些苹果能够到)
3.输出
大家可不要不耐烦,因为我这里用了一种不同寻常的方法
#include <iostream> using namespace std; int a[20],h,s; int main(){ for(int i=0;i<10;i++)cin >> a[i]; cin>>h; h+=30; for(int i=0;i<10;i++)s+=!(h<a[i]); cout<<s; return 0; }
大家看到这个代码有什么不一样吗?
对,就是看能够到那些苹果的地方可能与大家不一样
但这里也是一个逻辑结合的问题
相信大家都是这样写的:
#include <iostream> using namespace std; int a[20],h,s; int main(){ for(int i=0;i<10;i++)cin >> a[i]; cin>>h; h+=30; for(int i=0;i<10;i++){ if(h>=a[i]){ s++; } } cout<<s; return 0; }
但是不仅是逻辑的问题,给大家看一下时间
60ms 681kb
60ms 740kb
都是第一次运行的结果(IDE运行),也有运气的原因,但没有太大的运气差距
上面的是第一个代码的,下面的是第二个代码的
省了不少内存
所以说,逻辑思维的优化对题目对代码也会有很大好处
给大家讲一下逻辑思维吧
首先,我们知道,即便是第二种写法,发现能够到时,能够到的苹果数也只是+1
而我们的判断语句的结果不是0就是1
那么我们可以完美的结合起来
既然判断出来再加上的与判断出来的都是1,
那么我们为什么不把这两个结合起来呢?
结合起来就是“加上判断的”
那么我们来看一下对不对
s+=(h>=a[i]);
大家肯定都是这样想的吧?
但是我们再换一种思路
虽然只是改变了一点点,但是思路改变了一大块
s+=!(h<a[i]);
第一种是这样写的
总数=总数+总高度是否大于等于苹果的高度
第二种是这样写的
总数=总数+总高度是否不小于苹果的高度
看出不一样了吗?
当然!
我们在第二种的基础上
再来改变代码!
#include <iostream> using namespace std; int a[20],h,s; int main(){ for(int i=0;i<10;i++)cin >> a[i]; cin>>h; h+=30; for(int i=0;i<10;i++)s+=!(h<a[i]); cout<<s; return 0; }
这是原先的源代码
#include <iostream> using namespace std; int a[20],h,s; int main(){ for(int i=0;i<10;i++)cin >> a[i]; cin>>h; h+=30; for(int i=0;i<10;i++)s+=(h<a[i]); cout<<10-s; return 0; }
这是后来
没错!
我们这次的s改变了,s原本记录的是小于等于总高度的苹果数,但是我们这里的s记录的是大于总高度苹果的数
这10个苹果无非就三种情况:大于总高度、小于总高度、等于总高度
但是我们记录了大于的,那么剩下的也就是小于和等于了
怎么样?大家有没有被绕晕了呢?反正我是晕了
三、写一个稍稍稍稍稍难一点的代码
https://www.luogu.com.cn/problem/P1138
这道题相比之前是稍难一点了
因为用到了排序
不过对于我们来说还是小菜亿碟嘛(滑稽)
鉴于这道题是让输出第k小的整数,我们就用STL来做吧
因为要去重、排序
我们就用sort和unique
而且这个是从小到大排序的,所以我们的sort都不用写cmp了
先来一段稍稍稍稍稍稍稍稍难亿点的烧脑推理(这句话咋有点绕)
首先,我们假设有n个数,那么假设要求第k个数
我们首先要将这n个数排列
这n个数我们把他合并成a数组
从小到大去排列后
a数组的长度不变,顺序变了
由于可能会有重复的数,所以我们需要将重复的数去掉
a数组的长度变了,顺序没变
那么此时,a[k]就是第k大的数了
但是题目中说过,去重之后,数组a的长度可能会小于k
所以我们需要检测一下
如果去重后的长度小于k
那么就输出NO RESULT
不然的话才可以输出a[k]
代码送上
#include<bits/stdc++.h> using namespace std; int n,k,a[100000]; int main(){ cin>>n>>k; k--; for(int i=0;i<n;i++){ cin>>a[i]; } sort(a,a+n); int abc=unique(a,a+n)-a; if(k<abc){ cout<<a[k]; }else cout<<"NO RESULT"; return 0; }
好的我们可以看到
这里的abc是数组去重后的长度
然后一做比较,就可以输出了
当然这道题你也可以用桶
桶排的话就可以免去重了
应该会更省时间,这里就不多介绍了
四、总结
模拟这个算法就讲到这里
模拟呢只是逻辑的问题,要学会结合常数、不定数、公式以及思维逻辑来优化代码,说不定仔细钻研一下就能从WA或者TLE变成AC了
给大家理一下错误原因
(1)大小比较的逻辑
(2)代码优化
(3)合理、准确运用算法
(3)数据范围
那么今天到这里,欢迎下次再来看qwq