模拟其实就是根据题目指定的规则进行编程,非常锻炼写代码的能力。
今天首先讲到了日期相关的模拟,比如数天数,星期几的计算。这些题目主要是要特判一下闰年的2月份多了一天。
一开始,叫同学练习了时光倒流这道题。
题目描述:
JM已知当前的日期是y年m月d日,由于种种原因,JM希望时光能够倒流,回到n天前。不过由于时光机的问题,JM需要知道n(n<=20000)天之前是具体什么日期,你能帮帮JM吗?
由于n比较小,我们完全可以模拟一天天倒退来解决这道题。先对天数-1,如果天数等于0,说明回到上个月了,所以要将月份-1,同理,月份等于0,说明回到上一个年份了,且此时将月份置为12,然后利用月份确定天数,最后特判一下是否是2月且当前年份是闰年,如果是则补上一天。AC代码如下。
#include<bits/stdc++.h>
using namespace std;
int days[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int main()
{
int y,m,d,n;
scanf("%d-%d-%d",&y,&m,&d);
scanf("%d",&n);
while(n--)
{
d--;
if(d==0)
{
m--;
if(m==0)
{
y--;
m=12;
}
d=days[m];
if(m==2&&(y%400==0||(y%100!=0&&y%4==0))) d++;
}
}
printf("%d-%d-%d",y,m,d);
return 0;
}
然后我们了解算法复杂度的概念,所谓算法复杂度其实就是估计程序的运行次数,以便预测写出来的程序是否会TLE。一般地,一层循环能解决的问题,我们称为O(n),即使n=10000000,其1秒也可以过,二层循环,我们称之为O(n^2),一般n取3000以内,1秒可过。
接下来,我们讲到了高精度加法,高精度加法就是模拟小学生做算术,也属于模拟专题。
题目描述:
现在有一个简单的问题,给你两个正整数 A和 B,你需要计算出 A+B 的结果。不过要注意哦,这两个正整数非常大。A 和 B 的位数不超过 100000位。
思路:
首先我们知道char的范围是[-128,127],int的范围是[-2147483648,2147483647]最大能表示十位数的,long long的范围是[-9223372036854775808,9223372036854775807]最大能表示19位数。而本题的数非常大,有100000位,不能用普通的int和long long型解决。那我们用什么数据类型存这10万位的数字呢,比较直观的是用字符串类型存储。
那么问题来了,"12345"怎么和"678"做加法呢?我们首先需要将每一位对齐,比如个位'5'和'8'要对齐,但是从字符串的存储角度来讲,下标为0存的分别是'1'和'6',这显然是不对齐的,有一个巧妙的方法就是将"12345"和"678"先反转成"54321"和"876",这样下标为0存的分别是'5'和'8',个位刚好对齐了,同理十位百位也对其了,这样就比较方便做加法了。接下来我们将"54321"和"876"每一位分离用int数组存储,即5 4 3 2 1和8 7 6。然后再做加法结合进位得到3 2 0 3 1,最后倒置回来拼接成字符串就是最后的和13023。AC代码如下:
#include<bits/stdc++.h>
using namespace std;
string a,b;
int na[1000005],nb[1000005];
string add(string a,string b)
{
int la=a.size(),lb=b.size();
for(int i=0;i<la;i++) na[i]=a[la-i-1]-'0';
for(int i=0;i<lb;i++) nb[i]=b[lb-i-1]-'0';
int l=max(la,lb);
for(int i=0;i<l;i++) na[i]=na[i]+nb[i],na[i+1]+=na[i]/10,na[i]%=10;
string ans;
for(int i=(na[l]==0?l-1:l);i>=0;i--) ans+=char(na[i]+'0');
return ans;
}
int main()
{
cin>>a>>b;
cout<<add(a,b)<<endl;
return 0;
}
接下来,我们练习了高精度减法。
题目描述:
现在有一个简单的问题,给你两个正整数 A和 B,你需要计算出 A-B 的结果。不过要注意哦,这两个正整数非常大。A 和 B 的位数不超过 100000位。
思路:
在A+B的基础上略微修改即可,改成减法,注意借位,以及要注意,拼接前要用循环去除前导0 ,因为比如一个5位数减一个数字,结果可能是5位数,4位数,3位数,2位数,1位数,不像加法5位数加1位数只可能是5位数或6位数,所以加法用if就可以去除前导0,而减法必须用循环去除前导0。AC代码如下:
#include<bits/stdc++.h>
using namespace std;
string a,b;
int na[1000005],nb[1000005];
string sub(string a,string b)
{
int la=a.size(),lb=b.size();
for(int i=0;i<la;i++) na[i]=a[la-i-1]-'0';
for(int i=0;i<lb;i++) nb[i]=b[lb-i-1]-'0';
int l=max(la,lb);
for(int i=0;i<l;i++)
{
na[i]=na[i]-nb[i];
if(na[i]<0)
{
na[i+1]--;
na[i]+=10;
}
}
string ans;
while(na[l]==0&&l>0) l--;
for(int i=l;i>=0;i--) ans+=char(na[i]+'0');
return ans;
}
int main()
{
cin>>a>>b;
if(a.size()>b.size()||(a.size()==b.size()&&a>=b))
{
cout<<sub(a,b)<<endl;
}
else
{
cout<<"-";
cout<<sub(b,a)<<endl;
}
return 0;
}
接下来,我们讲到了高精度乘法
题目描述:
现在有一个简单的问题,给你两个正整数 A和 B,你需要计算出 A*B 的结果。不过要注意哦,这两个正整数非常大。A 和 B 的位数不超过 1000位。
思路:
和A+B类似,先倒序存储,然后对于第一个数第i位和第二个数第j位相乘,其结果应存在第i+j位里,而且要先乘,后面再统一处理进位。AC代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
string a,b;
int na[N],nb[N],nc[N];
int main ( )
{
cin>>a>>b;
int la=a.size(),lb=b.size();
for(int i=0;i<la;i++) na[i]=a[la-i-1]-'0';
for(int i=0;i<lb;i++) nb[i]=b[lb-i-1]-'0';
for(int i=0;i<la;i++)
{
for(int j=0;j<lb;j++)
{
nc[i+j]+=na[i]*nb[j];
}
}
for(int i=0;i<la+lb;i++)
{
nc[i+1]+=nc[i]/10;
nc[i]%=10;
}
int l=la+lb-1;
if(nc[l]==0) l--;
string ans;
for(int i=l;i>=0;i--)
ans+=char(nc[i]+'0');
cout<<ans<<endl;
return 0;
}
再接下来,带领同学们做了一道简单的幂运算
题目描述:
给定,,求和中较大值,其中
很显然,的具体结果得依靠高精度来计算,而计算的话,我们可以采取二分法来加速计算,比如2^100=2^50*2^50,这样问题就转化成求2^50,同理又转化成2^25,然后如果次幂是奇数,就拿一个2下来转化成求2^24,又转化成2^12以及2^6,2^3,2^2,直到2^1。这样比2*2*2......*2乘100次快多了。由于我这里x,y是以int读入,高精度又需要以string存储,所以得先将x,y的string形式求出来,那很简单,数位分离即可,然后倒序回来即可。然后利用递归来实现上述分解计算a^n。AC代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=8005;
int na[N],nb[N],nc[2*N];
int x,y;
string int2str(int n)
{
string a="",b="";
while(n)
{
a+=char(n%10+'0');
n/=10;
}
for(int i=a.size()-1;i>=0;i--)
b+=a[i];
return b;
}
string mul(string a,string b)
{
int la=a.size(),lb=b.size();
fill(nc,nc+N,0);
for(int i=0;i<la;i++) na[i]=a[la-i-1]-'0';
for(int i=0;i<lb;i++) nb[i]=b[lb-i-1]-'0';
for(int i=0;i<la;i++)
for(int j=0;j<lb;j++)
nc[i+j]+=na[i]*nb[j];
for(int i=0;i<la+lb;i++)
{
nc[i+1]+=nc[i]/10;
nc[i]%=10;
}
int l=la+lb-1;
if(nc[l]==0) l--;
string ans;
for(int i=l;i>=0;i--)
ans+=char(nc[i]+'0');
return ans;
}
string pow2(string x,int n)
{
if(n==1) return x;
if(n%2==0)
{
string tmp=pow2(x,n/2);
return mul(tmp,tmp);
}
else
{
n--;
string tmp=pow2(x,n/2);
return mul(mul(tmp,tmp),x);
}
}
int main()
{
cin>>x>>y;
string nx=int2str(x),ny=int2str(y);
string ans1=pow2(nx,y),ans2=pow2(ny,x);
if(ans1.size()>ans2.size()||(ans1.size()==ans2.size()&&ans1>=ans2))
{
cout<<ans1<<endl;
}
else
{
cout<<ans2<<endl;
}
return 0;
}
最后做了回形取数这道题
题目描述:
回形取数就是沿矩阵的边取数,若当前方向上无数可取或已经取过,则左转90度。一开始位于矩阵左上角,方向向下。输入第一行是两个不超过200的正整数m, n,表示矩阵的行和列。接下来m行每行n个整数,表示这个矩阵。输出只有一行,共m*n个数,为输入矩阵回形取数得到的结果。数之间用一个空格分隔,行末不要有多余的空格。
思路:
典型的按规则模拟,非常考验写代码的能力,通过观察,我们可以发现,就是先从上到下,从左到右,从下到上,从右到左打印一层又一层的圈。然后我们模拟即可,AC代码:
#include<bits/stdc++.h>
using namespace std;
int a[205][205];
int main ( )
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
}
int t=1,cnt=0;
while(1)
{
for(int i=t;i<=n-t+1;i++)
{
cout<<a[i][t]<<" ";
cnt++;
if(cnt==n*m) break;
}
if(cnt==n*m) break;
for(int i=t+1;i<=m-t+1;i++)
{
cout<<a[n-t+1][i]<<" ";
cnt++;
if(cnt==n*m) break;
}
if(cnt==n*m) break;
for(int i=n-t;i>=t;i--)
{
cout<<a[i][m-t+1]<<" ";
cnt++;
if(cnt==n*m) break;
}
if(cnt==n*m) break;
for(int i=m-t;i>=t+1;i--)
{
cout<<a[t][i]<<" ";
cnt++;
if(cnt==n*m) break;
}
if(cnt==n*m) break;
t++;
}
return 0;
}