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

posted @ 2020-08-05 15:05  无咕  阅读(1918)  评论(0编辑  收藏  举报
/* 点击爆炸效果*/