基础算法整理-模拟

这是一系列入门级和基础级算法总结的首篇,让我们聚焦到OI界最常见的入门级算法——模拟!

 模拟,顾名思义,就是让电脑按照题目所给出的方法来运行,最终输出所需要的结果的过程。

 在这个算法中,需要注意到的有以下几点:

  • 关于所使用的语言的是否熟悉(最为关键)
  • 关于问题的分析思路是否最简
  • 是否嫩在规定时间内模拟完整个过程并输出结果
  • 关于问题的分析方法是否易于实现(关乎比赛时能否在规定时间内打完代码并调完bug)
  • 对于特殊数据是否考虑周全

 以上便是我从近几年的竞赛经验中总结出的几点需要注意的地方。

 下面来看几个例题:

  T1.玩具谜题(NOIP2016 D1T1)

 本题是近几年来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轻喷……

 

  T2.神奇的幻方(NOIP2015 D1T1)

 没有很大难度,直接按照题中所给的方案填充幻方即可

 代码如下:

 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 }

//没什么好说的……

  T3.Vigenère密码(NOIP2012 D1T1)

 字符替换……就这样,代码如下:

 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 }

//不说了……

   T4.洛谷题库P1906 凯撒密码

 怎么说呢,这题数据有鬼,大概一部分是在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 }

//还是想问一下是不是码风很丑

   T5.时间复杂度(NOIP2017 D1T2)

 训练时看到这题,突然激动起来。写了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(机房里一个大佬的名字)

  关于模拟就说到这里,本人认为:只要选手对语言足够熟悉,写程序时足够细心,没有什么纯模拟题是调不出来的……算法看完了,剩下的就是训练了!大家加油!

posted @ 2018-10-24 00:15  氢离子活度指数  阅读(516)  评论(0)    收藏  举报