2024届信息学竞赛 模拟专题总结

模拟其实就是根据题目指定的规则进行编程,非常锻炼写代码的能力。

今天首先讲到了日期相关的模拟,比如数天数,星期几的计算。这些题目主要是要特判一下闰年的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;
}

再接下来,带领同学们做了一道简单的幂运算

题目描述:

给定x,y,求x^yy^x中较大值,其中1<=x,y<=2000

很显然,x^y的具体结果得依靠高精度来计算,而计算x^y的话,我们可以采取二分法来加速计算,比如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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值