模拟算法
模拟算法是一种最基本的算法思想,是对程序员基本编程能力的一种考查,其解决方法就是根据题目给出的规则对题目要求的相关过程进行编程模拟。在解决模拟类问题时,需要注意字符串处理、特殊情况处理和对题目意思的理解。仔细分析题目给出的规则,要尽可能地做到全面地考虑所有可能出现的情况,这是解模拟类问题的关键点之一。
1、字符串的展开
NC16644 字符串的展开
题目描述:
在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于“d-h”或“4-8”的子串,我们就把它当作一种简写,输出时,用连续递增的字母或数字串替代其中的减号,即,将上面两个子串分别输出为“defgh”和“45678”。在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活。具体约定如下:
(1)遇到下面的情况需要做字符串的展开:在输入的字符串中,出现了减号“-”,减号两侧同为小写字母或同为数字,且按照ASCII码的顺序,减号右边的字符严格大于左边的字符。
(2)参数 p1 :展开方式。p1=1 时,对于字母子串,填充小写字母;p1=2
时,对于字母子串,填充大写字母。这两种情况下数字子串的填充方式相同。p1=3
时,不论是字母子串还是数字子串,都用与要填充的字母个数相同的星号“*”来填充。 (3)参数 p2:填充字符的重复个数。p2=k
表示同一个字符要连续填充 kk 个。例如,当 p2=3 时,子串“d-h”应扩展为“deeefffgggh”。减号两侧的字符不变。
(4)参数 p3:是否改为逆序:p3 =1 表示维持原有顺序,p3 =2
表示采用逆序输出,注意这时仍然不包括减号两端的字符。例如当 p·=1、p2=2、p3=2
时,子串“d-h”应扩展为“dggffeeh”。
(5)如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:“d-e”应输出为“de”,“3-4”应输出为“34”。如果减号右边的字符按照ASCII码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:“d-d”应输出为“d-d”,“3-1”应输出为“3-1”。
输入描述:
第 1 行为用空格隔开的 3 个正整数,依次表示参数 p1,p2,p3 。 第 2
行为一行字符串,仅由数字、小写字母和减号“-”组成。行首和行末均无空格。
输出描述:
输出一行,为展开后的字符串。
示例1
输入
1 2 1 abcs-w1234-9s-4zz
输出
abcsttuuvvw1234556677889s-4zz
示例2
输入
2 3 2 a-d-d
输出
aCCCBBBd-d
示例3
输入
3 4 2 di-jkstra2-6
输出
dijkstra2************6
备注:
40% 的数据满足:字符串长度不超过5
100% 的数据满足:1 ≤ p1 ≤ 3, 1 ≤ p2≤ 8, 1 ≤ p3≤ 2。字符串长度不超过100
解题思路:
按照题目意思模拟即可
1、翻转可以直接使用reverse函数
2、注意细节
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int p1,p2,p3;
cin>>p1>>p2>>p3;
string s;
cin>>s;
string ans;
for(int i=0;i<s.size();i++){
if(i>0&&i<s.size()-1&&s[i]=='-'&&s[i-1]<s[i+1]&&(s[i-1]>='0'&&s[i+1]<='9'||s[i-1]>='a'&&s[i+1]<='z')){//满足转换条件
string s2;
for(char c=s[i-1]+1;c<s[i+1];c++){
for(int j=0;j<p2;j++){
if(p1==1){
s2+=c;
}else if(p1==2){
if(c>='a'&&c<='z'){//p1等于2小写转大写
s2+=c-'a'+'A';
}else{
s2+=c;
}
}else{//p1等于3转成*号
s2+='*';
}
}
}
if(p3==2){//p3等于2时翻转
reverse(s2.begin(),s2.end());
}
ans+=s2;
}else{
ans+=s[i];
}
}
cout<<ans;
}
2、多项式输出
NC16622 多项式输出
题目描述
一元n次多项式可用如下的表达式表示:
f (x) = anxn+ an-1xn-1 + … + a1x + a0,a0≠0
其中,aixi 称为i 次项,ai称为i次项的系数。给出一个一元多项式各项的次数和系数,请按照如下规定的格式要求输出该多项式:
- 多项式中自变量为x,从左到右按照次数递减顺序给出多项式。
- 多项式中只包含系数不为0的项。
- 如果多项式n次项系数为正,则多项式开头不出现“+”号,如果多项式 n 次项系数为负,则多项式以“-”号开头。
- 对于不是最高次的项,以“+”号或者“-”号连接此项与前一项,分别表示此项系数为正或者系数为负。紧跟一个正整数,表示此项系数的绝对值(如果一个高于0 次的项,其系数的绝对值为1,则无需输出1)。如果x的指数大于1,则接下来紧跟的指数部分的形式为“x^b”,其中b为x的指数;如果x的指数为1,则接下来紧跟的指数部分形式为“x”;如果x的指数为0,则仅需输出系数即可。
- 多项式中,多项式的开头、结尾不含多余的空格。
输入描述:
第一行1个整数,n,表示一元多项式的次数。
第二行有n+1 个整数,其中第i 个整数表示第n-i+1 次项的系数,每两个整数之间用空格隔开。
输出描述:
共1行,按题目所述格式输出多项式。
示例1
输入
5 100 -1 1 -3 0 10
输出
100x^5-x^4+x^3-3x^2+10
示例2
输入
3 -50 0 0 1
输出
-50x^3+1
备注:
1≤n≤100,多项式各次项系数的绝对值均不超过100。
解题思路:
1、常数为1时需要省略
2、注意前缀不需要+号,如果是负数时需要-号
3、常数为0省略该项
按照题目要求模拟即可,具体解释可见代码
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
string ans;
int c;
for(int i=n;i>=0;i--){
cin>>c;
if(c==0){//底数为0则跳过
continue;
}
if(i!=n&&c>=0)ans+='+';//整数加上+号
ans+=to_string(c);
if((c==1||c==-1)&&i!=0)ans.pop_back();//删除多余的1
if(i!=0){
ans+="x^";
if(i==1){
ans.pop_back();//删除多余的^号
}else{
ans+=to_string(i);//加上数字
}
}
}
cout<<ans<<endl;
}
3、机器翻译
NC16589 机器翻译
题目描述
小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。
这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。
假设内存中有 M 个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过
M−1,软件会将新单词存入一个未使用的内存单元;若内存中已存入M 个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。假设一篇英语文章的长度为 NN 个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。
输入描述:
输入共 2 行。每行中两个数之间用一个空格隔开。
第一行为两个正整数 MM 和 N,代表内存容量和文章的长度。
第二行为 N个非负整数,按照文章的顺序,每个数(大小不超过 1000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。
输出描述:
共 1 行,包含一个整数,为软件需要查词典的次数。
示例1
输入
3 7 1 2 1 5 4 4 1
输出
5
说明
整个查字典过程如下:每行表示一个单词的翻译,冒号前为本次翻译后的内存状况:
空:内存初始状态为空。
1. 1:查找单词1 并调入内存。
2. 1 2:查找单词2 并调入内存。
3. 1 2:在内存中找到单词1。
4. 1 2 5:查找单词5 并调入内存。
5. 2 5 4:查找单词4 并调入内存替代单词1。
6. 2 5 4:在内存中找到单词4。
7. 5 4 1:查找单词1 并调入内存替代单词2。
共计查了5次词典。
备注:
对于 10% 的数据有 M=1,N ≤5M=1,N≤5。
对于 100% 的数据有 0<M ≤ 1000<M≤100,0<N ≤ 10000<N≤1000
解题思路:
1、需要用一个集合来存目前已经有的单词
2、需要用一个双端队列存出现过的单词,当集合插入新的单词时,同时把该单词添加进队列中,当需要删除时,按队列顺序删除元素。
3、当集合满了的时候,再次添加单词时,需要根据双端队列删除最先加入的单词
4、值得注意的是,不可单纯地用指针在原数组中滑动来代替双端队列,因为要维护集合与队列的一致性,集合里有什么单词,队列里也有什么单词
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m;
cin>>n>>m;
unordered_set<int> s;//无序集合
int a[1010];
for(int i=0;i<m;i++){
cin>>a[i];
}
int ans=0;
deque<int> q;//双端队列
for(int i=0;i<m;i++){
if(s.count(a[i])){//如果已经存在,则不需要查询,跳过
continue;
}else{//查询
ans++;
s.insert(a[i]);//添加目前的单词
q.push_back(a[i]);//添加到队列中,后面要按顺序删除
}
if(s.size()>n){
s.erase(q.front());//如果存在则删除,否则跳过
q.pop_front();//从前到后删除
}
}
cout<<ans<<endl;
}
是不是很简单呢?
刚接触肯定会觉得难,多些做题多些用,熟悉了就容易了,兄弟萌,加油!!!
文章尚有不足,欢迎大牛们指正
感谢观看,点个赞吧