基础算法整理-模拟
这是一系列入门级和基础级算法总结的首篇,让我们聚焦到OI界最常见的入门级算法——模拟!
模拟,顾名思义,就是让电脑按照题目所给出的方法来运行,最终输出所需要的结果的过程。
在这个算法中,需要注意到的有以下几点:
- 关于所使用的语言的是否熟悉(最为关键)
- 关于问题的分析思路是否最简
- 是否嫩在规定时间内模拟完整个过程并输出结果
- 关于问题的分析方法是否易于实现(关乎比赛时能否在规定时间内打完代码并调完bug)
- 对于特殊数据是否考虑周全
以上便是我从近几年的竞赛经验中总结出的几点需要注意的地方。
下面来看几个例题:
本题是近几年来NOIP赛事中最简单的一题(然而那时的我过于天真,不会写string……)
本题主要关注每个小人朝向内外状态与向左/右数的关系,一个显而易见的优化是当向一边数的人数si超过整个圈总人数n时,si=si%n
废话不多说,贴代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<string> 5 #define inputcheck 0 6 #define poscheck 1 7 using namespace std; 8 struct toy{ 9 int dir; 10 string name; 11 }a[100001]; 12 int n,m,pos=1; 13 void move(int t_dir,int t_mov) 14 { 15 if((a[pos].dir+t_dir)%2==0) {pos=pos+n-t_mov; return;} 16 if((a[pos].dir+t_dir)%2==1) pos=pos+t_mov; 17 } 18 int main() 19 { 20 cin>>n>>m; 21 for(int i=1;i<=n;i++) 22 { 23 cin>>a[i].dir>>a[i].name; 24 getchar(); 25 } 26 int dir,num; 27 for(int i=1;i<=m;i++) 28 { 29 cin>>dir>>num; 30 move(dir,num); 31 while(pos<=0) pos=pos+n; 32 if(pos>n) pos=pos%n; 33 } 34 cout<<a[pos].name; 35 return 0; 36 }
//写的很乱,经验不足请dalao轻喷……
没有很大难度,直接按照题中所给的方案填充幻方即可
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstring> 5 #include<string> 6 #include<algorithm> 7 const int N = 50; 8 using namespace std; 9 int map[N][N],n; 10 int main() 11 { 12 scanf("%d",&n); 13 map[1][(n+1)/2]=1;//行+列 14 int precol=1,prelin=(n+1)/2;//col:行 lin:列 15 for(int i=2;i<=n*n;i++) 16 { 17 //int nowcol,nowlin; 18 if(precol==1&&prelin!=n) 19 { 20 map[n][prelin+1]=i; 21 precol=n; 22 prelin+=1; 23 continue; 24 } 25 if(prelin==n&&precol!=1) 26 { 27 map[precol-1][1]=i; 28 precol-=1; 29 prelin=1; 30 continue; 31 } 32 if(precol==1&&prelin==n) 33 { 34 map[2][n]=i; 35 precol=2; 36 prelin=n; 37 continue; 38 } 39 else 40 { 41 if(!map[precol-1][prelin+1]) 42 { 43 map[precol-1][prelin+1]=i; 44 precol=precol-1; 45 prelin=prelin+1; 46 continue; 47 } 48 else 49 { 50 map[precol+1][prelin]=i; 51 precol=precol+1; 52 continue; 53 } 54 } 55 } 56 for(int i=1;i<=n;i++) 57 { 58 for(int j=1;j<=n;j++) 59 { 60 printf("%d ",map[i][j]); 61 } 62 printf("\n"); 63 } 64 return 0; 65 }
//没什么好说的……
字符替换……就这样,代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 6 using namespace std; 7 8 string key,code; 9 10 bool is_big(char i) 11 { 12 if(i>='A'&&i<='Z') return 1; 13 else return 0; 14 } 15 16 void tranprint(int i) 17 { 18 if(is_big(code[i])) printf("%c",(code[i]-'A'-key[i%key.length()]+52)%26+'A'); 19 else printf("%c",(code[i]-'a'-key[i%key.length()]+52)%26+'a'); 20 } 21 22 int main() 23 { 24 cin>>key>>code; 25 for(int i=0;i<key.length();i++) if(is_big(key[i])) key[i]-='A'; else key[i]-='a'; 26 for(int i=0;i<code.length();i++) tranprint(i); 27 return 0; 28 }
//不说了……
怎么说呢,这题数据有鬼,大概一部分是在Windows下造的,另一部分是在Linux下造的,导致使用getline语句时会出错,调了很久最后使用cin才过……
代码如下:
1 #include<cstdio> 2 #include<string> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 string str[50050]; 8 int chanum[27]={0}; 9 void count(string str) 10 { 11 // cout<<"WORK:"<<str<<endl; 12 int l=str.length(); 13 for(int i=0;i<l;i++) 14 { 15 if(str[i]<='z'&&str[i]>='a') chanum[str[i]-'a']++; 16 if(str[i]<='Z'&&str[i]>='A') chanum[str[i]-'A']++; 17 } 18 } 19 int getans() 20 { 21 // cout<<"GET ANS"<<endl; 22 int max=0,maxp; 23 for(int i=1;i<=25;i++) 24 { 25 if(max<=chanum[i]) 26 { 27 max=chanum[i]; 28 maxp=i; 29 } 30 } 31 return maxp; 32 } 33 bool eof(string str) 34 { 35 string kaka("ENDOFINPUT"); 36 if(str.length()!=10) return 0; 37 for(int i=0;i<str.length()-1;i++) 38 { 39 if(str[i]!=kaka[i]) return 0; 40 } 41 return 1; 42 } 43 bool sta(string str) 44 { 45 string kaka("START"); 46 if(str.length()!=5) return 0; 47 for(int i=0;i<str.length()-1;i++) 48 { 49 if(str[i]!=kaka[i]) return 0; 50 } 51 return 1; 52 } 53 bool end(string str) 54 { 55 string kaka("END"); 56 if(str.length()!=3) return 0; 57 for(int i=0;i<str.length()-1;i++) 58 { 59 if(str[i]!=kaka[i]) return 0; 60 } 61 return 1; 62 } 63 int main() 64 { 65 // string Eof("ENDOFINPUT"),Sta("START"); 66 int strn=0; 67 string tmpstr; 68 cin>>tmpstr; 69 while(!eof(tmpstr)) 70 { 71 if(eof(tmpstr)) break; 72 // cout<<"INPUT "<<tmpstr<<" "<<tmpstr.length()<<" "<<Sta.length()<<endl; 73 if(sta(tmpstr)) 74 { 75 while(!end(tmpstr)) 76 { 77 cin>>tmpstr; 78 if(end(tmpstr)) break; 79 str[strn]=str[strn]+" "+tmpstr; 80 } 81 // cout<<"STR: "<<str[strn]<<str[strn].length()<<endl; 82 // cout<<str[strn]<<endl; 83 count(str[strn]); 84 strn++; 85 } 86 cin>>tmpstr; 87 } 88 int t=getans(); 89 // cout<<strn<<" "<<endl; 90 for(int i=0;i<strn;i++) 91 { 92 for(int j=1;j<str[i].length();j++) 93 { 94 if(str[i][j]<='z'&&str[i][j]>='a') {cout<<(char)((str[i][j]-'a'-t+4+26)%26+'A'); continue;} 95 if(str[i][j]<='Z'&&str[i][j]>='A') {cout<<(char)((str[i][j]-'A'-t+4+26)%26+'A'); continue;} 96 cout<<str[i][j]; 97 } 98 cout<<endl; 99 } 100 }
//还是想问一下是不是码风很丑
训练时看到这题,突然激动起来。写了20来分钟,突然感觉自己回到了去年的考场上,刚刚看完小凯的疑惑一脸懵逼,然后看到这题时的绝望……
其实这题不是很难,只需用一个栈维护一个个for语句,(我的写法)并在栈中每个语句加上标记显示当前语句是否会被执行,若上层(栈顶)语句不会被执行,则后续各语句都不会被执行。
了解这个之后就可以开始写代码了。一定要小心细致,否则这个题目出了bug也是挺难调试的。
还有一点,每组数据都要读完,一行都不能剩下!看到别人的题解都说自己被这个奇怪的问题卡过,所以在此多提一句...
具体参见源码:
1 #include<cmath> 2 #include<stack> 3 #include<cstdio> 4 #include<string> 5 #include<cstring> 6 #include<sstream> 7 #include<iostream> 8 #include<algorithm> 9 int max(int a,int b) 10 { 11 return (a>b)?a:b; 12 } 13 using namespace std; 14 int Err=0;//记录当前判断的代码的正确性 15 bool vused[300]={0}; 16 struct Cir{ 17 int start,final,var,isv,dep; 18 //isv --> 该层循环是1否0会执行 dep --> 当前循环语句所处复杂度 19 }; 20 stack<Cir> Sta; //用栈来存储循环深度 21 int AnsAna(const string &str)//输入复杂度字符串的判断 返回0为常数复杂度 返回其它为复杂度的幂数 22 { 23 int l=str.length(); 24 if(l==3) return 0;//如果复杂度长度为4(3+1)即为O(1) 时间复杂度幂数为0 25 int ans=0; 26 for(int i=4;i<l-1;i++) 27 { 28 ans=ans*10+str[i]-'0'; 29 } 30 return ans;//返回幂数 31 } 32 void Input()//输入判断 33 { 34 char var; 35 int sta=0,end=0,isv=1,dep=0; 36 string Fstr; 37 cin>>var; 38 if(vused[var-'a'+1]) {Err=1; /*printf("****$Chongfu$ used vars\n");*/} //重复使用的变量导致错误 39 vused[var-'a'+1]=1; 40 cin>>Fstr; 41 if(Fstr=="n") sta=-1;//循环开始和结束量为n时用-1代替 42 else sscanf(Fstr.c_str(),"%d",&sta);//从字符串读入数字 43 cin>>Fstr; 44 if(Fstr=="n") end=-1; 45 else sscanf(Fstr.c_str(),"%d",&end); 46 if((sta==-1&&end!=-1)||(sta!=-1&&end!=-1&&sta>end)||(!((Sta.empty())?1:Sta.top().isv))) {isv=0; /*printf("****Unable to Enter...\n");*/} else isv=1; //当前循环不会执行,isv=0; 47 if(Sta.empty()) dep=0; else dep=Sta.top().dep;//如果栈为空 当前循环语句深度为0 否则深度为上个循环对应的深度 48 if(sta!=-1&&end==-1) dep++; //如果循环起始量不为n且终止量为n 当前循环层数+1 49 getline(cin,Fstr);//读入F语句剩余部分 50 cout<<Fstr<<endl; 51 int l=Fstr.length();//确定字符串长度 52 var=Fstr[1]; 53 if(vused[var]) Err=1;//如果该变量还在使用,报错 54 vused[var]=1;//如果未被使用,标记其为被使用 55 int pos; 56 for(pos=3;pos<l-1;pos++)//读取循环起始量 57 { 58 if(Fstr[pos]=='n') {sta=-1; break;}//如果起始量为n 59 if(Fstr[pos]==' ') break; 60 sta=sta*10+Fstr[pos]-'0';//记录起始量 61 } 62 for(;pos<l-1;pos++) 63 { 64 if(Fstr[pos]=='n') {end=-1; break;}//如果终止量为n 65 if(Fstr[pos]==' ') break; 66 end=end*10+Fstr[pos]-'0';//记录终止量 67 } 68 */ 69 // printf("****>>F %c %d %d %d\n",var,sta,end,dep); 70 Sta.push(Cir{sta,end,var,isv,dep});//将当前循环内容压入栈 71 } 72 int main() 73 { 74 int t; 75 cin>>t; 76 while(!Sta.empty()) Sta.pop();//清空栈(初始化) 77 for(int i=1;i<=t;i++) 78 { 79 int tcom=0; 80 Err=0; 81 while(!Sta.empty()) Sta.pop();//初始化栈 82 memset(vused,0,sizeof(vused));//初始化变量名标记数组 83 int lines; 84 string Com; 85 cin>>lines>>Com;//读入行数和复杂度 86 char ord; 87 for(int i=1;i<=lines;i++) 88 { 89 cin>>ord; 90 if(ord=='F') Input();//如果是F语句 控制输入参数 91 else //如果是E语句 92 { 93 if(Sta.empty()) {Err=1; continue; /*printf("****One E missed?\n");*/} //如果当前栈为空 报错并跳过 94 Cir ajil=Sta.top(); //取栈顶元素 95 vused[ajil.var-'a'+1]=0;//解除对该层循环的限制 96 if(!ajil.isv) {Sta.pop(); continue;}//如果该层循环不被执行 弹出该层并继续 97 if(ajil.start!=-1&&ajil.final==-1) {tcom=max(tcom,ajil.dep); Sta.pop(); continue;} //起始量不为n 而 终止量为n --> 循环层数+1 98 if(ajil.start!=-1&&ajil.final!=-1&&ajil.start<=ajil.final) {Sta.pop(); continue;} //起始量与终止量均不为n 且 起始量小于等于终止量 --> 循环层数不变 99 if(ajil.start!=-1&&ajil.final!=-1&&ajil.start>=ajil.final) {tcom=max(tcom,ajil.dep); Sta.pop(); continue;} //起始量与终止量均不为n 且 起始量大于终止量 --> 循环层数清零 100 if(ajil.start==-1&&ajil.final==-1) {Sta.pop(); continue;} //起始量为n 且 终止量为n --> 循环层数不变 101 } 102 } 103 if(!Sta.empty()) {Err=1; /*printf("****At the end with no empty stack\n");*/}//如果整个程序读入完毕后栈中还有元素剩余 FE不匹配 报错 104 if(Err) {printf("ERR\n"); continue;} //如果之前寻找到了错误 105 if(AnsAna(Com)==tcom) printf("Yes\n"); //如果输入的复杂度与程序实际复杂度相同 输出Yes 106 else printf("No\n"); 107 Err=0;//错误清零 108 } 109 return 0; 110 }
//具体实现方案看注释……也没有更好的解释了,请手动忽略变量名ajil(机房里一个大佬的名字)
关于模拟就说到这里,本人认为:只要选手对语言足够熟悉,写程序时足够细心,没有什么纯模拟题是调不出来的……算法看完了,剩下的就是训练了!大家加油!

浙公网安备 33010602011771号