算法学习入门---模拟(C++)
目录
1.洛谷---多项式输出
2.洛谷---蛇形方阵
3.洛谷---字符串展开
1.洛谷---多项式输出

一项一项输出,每一项输出关心3个部分:符号+系数+次数
- 符号:其他时间不用管,除了第一次输出,如果为正则跳过输出,为负正常输出
- 系数:为 1 时跳过输出,当次数为 0 时,即使为 1 也要正常输出;为 0 时符号一整段跳过输出(break语句)
- 次数:第一个未知数的次数为 n
- 当输出完符号后,系数的符号就没用了,所以可以abs取系数的绝对值
- 字符串变量不能和整型变量相加,c++中未定义过这样的操作
代码:
#include<iostream>
#include<vector>
#include<string>
#include<cmath>
using namespace std;string ret;int main()
{int n;cin>>n;vector<int> num(n+1,0);for(int i=n;i>=0;i--)cin>>num[i];for(int i=n;i>=0;i--){//系数为0,什么都不用考虑了 if(!num[i]) continue;else{//符号判断 if(num[i]<0) ret+='-';else{if(i!=n) ret+='+';}//系数判断int flag = abs(num[i]);if(flag!=1||(flag==1&&i==0)) ret+=to_string(flag);//次数判断if(i>1) ret = ret + "x^" + to_string(i);else if(i==1) ret += "x";} }cout<<ret;return 0;
}
代码易错点:
请注意,x的次数为1时,是不需要加上 ^1 的!
2.洛谷---蛇形方阵

注意事项:注意每个数字有都会占用 3 个字符,前面使用空格补齐
解决思路:
- 定义方向向量:先定义一个坐标轴,因为原点是在左上角,所以需要把整个坐标轴往右旋转90°,如下图1所示;定义两个数组,dx 和 dy ,dx = {0,0,1,-1} dy = {1,-1,0,0} ;此时如果从(2,3)往右走一个单位 -> (2,4)= (2,3)+ dx[0] + dy[0]
- 根据规则结合方向向量填数:朝一个方向走,边走边填数;越界之后,结合方向向量,重新计算出新的坐标以及方向
- 数组从第1行第1列开始算,这样就可以简化很多的数组越界问题
- 边界情况:y <= n && x <= n && x >= 1 && y >= 1 && arr[x][y] == 0
- dx、dy数组最好是以右下左上的顺序出现,并定义一个变量pos,只要不符合上面的边界情况,pos ++;但如果 pos 从最后一个的情况(往上移动,如下图2所示),变回了第1种情况(往右移动),此时数组会越界,所以需要 pos = (pos+1)%4
- 总共要填n行n列个数字,即为 n * n 个数字
- %0 /0 不会编译报错,但在C++中这是未定义行为,通常会导致程序崩溃或死循环

图1

图2
代码:
#include<iostream>
#include<vector>
using namespace std;const int N = 30;
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};//右下左上
int ret[N][N];int main()
{int n;cin>>n;int x=1,y=1;//初始位置int count=1;//当前数值 int pos = 0;while(count<=n*n){//正常情况 if(y >= 1 && y <= n && x <= n && x >= 1 && ret[x][y] == 0) ret[x][y]=count;else//越界情况 {x-=dx[pos];y-=dy[pos];//出现越界了还要退回到原来位置 pos = (pos+1)%4; count--;//字符还没有填,所以需要和后面那个count++抵消掉 } x+=dx[pos];y+=dy[pos];count++;}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%3d",ret[i][j]); cout<<endl;}return 0;
}
代码易错点:
如何输出占用3个字符的数字?
printf("%3d",ret[i][j]);
3.洛谷---字符串展开

对于条件很多的模拟题,我们可以把各种条件先写下来
- p1 : =1 -> 填小写 && =2 -> 填大写 && =3 -> 填 * 号
- p2 : =1 -> 填 1 个 && =n -> 填 n 个
- p3 : =1 -> 顺序填写 && =2 -> 逆序填写
同时还有一些注意事项:
当 - 号两边只差了1(例如d和e),那么直接把 - 号去掉就行;当 - 号两边为同一个字符,那么直接原封不动地输出 - 号;如果 - 号左边的字符大于右边的字符,也是直接输出 - 号;当不为 - 号,直接输出即可,因为需要修改填充的只有 - 号;当 - 号出现在字符串开头和结尾,直接输出
代码:
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;int p1,p2,p3;
string a;
string ret;bool islet(char ch)
{return ch>='a'&&ch<='z';
}bool isnum(char ch)
{return ch>='0'&&ch<='9';
}void add(char left,char right)
{//开始展开string r;for(char ch=left+1;ch<right;ch++){char tmp = ch;//处理p1if(p1==2&&islet(tmp)) tmp-=32;else if(p1==3)tmp = '*';//处理p2for(int turn=1;turn<=p2;turn++)r+=tmp; } //处理p3 if(p3==2)reverse(r.begin(),r.end());ret+=r;
}int main()
{cin>>p1>>p2>>p3>>a;int n = a.size();for(int i=0;i<=n-1;i++){//不是 - 号,或者 - 号出现在开头和结尾if(a[i]!='-'||i==0||i==n-1) ret+=a[i];else{char left=a[i-1],right=a[i+1];//islet函数为判断字符是否为字母,isnum函数为判断字符是否为数字 if((islet(left)&&islet(right)&&right>left)||(isnum(left)&&isnum(right)&&right>left)){//字符串展开add(left,right); }else{ret+=a[i];}}}cout<<ret;return 0;
}
代码易错点:
写这种模拟题真的会有非常多的条件需要判断,因此我们可以通过函数来进行模块的解耦,最好不要出现屎山代码(会难以调试与理解)当我们直接cout (a-a+A)时,输出的不会是字符A,而是A的asc码值65
小写字母变大写字母,减去32号即可(a的asc码为97,A的asc码为65)
