随着科技的发展和时代的进步,信息技术越来越被人们所重视,也有越来越多的同学被程序设计带来的无穷魅力所吸引。
首先给大家看一道题:
给定一正整数n,输出任意一组解x、y、z,使得
。如果不存在,则输出No Solution。
可能很多同学看到这题以后首先想到枚举算法。但是这里有3个变量,而且每个变量的范围没有上限,根本无法求解。其实,只要知道费马大定理这题就迎刃而解了。
费马大定理描述如下:当整数n>2时,对于所有正整数x,y,z都有
。
它的另外一种表述是:方程
在n>2时没有非零的整数解。所以,当n>2时,我们可以直接输出No Solution。当n=1或2时,任意输出一解即可。
var
n:integer;
begin
readln(n);
if n=1 then writeln('1 2 3')
else if n=2 then writeln('3 4 5')
else writeln(‘No Solution’);
end.
大家从上面的例子中可能已经感受到数学思想在程序设计中的重要性了。下面我们具体来谈一谈。
一、直接用数学思想解决问题
有些题目是以数学题本身呈现的,所以不要犹豫,直接用数学思想搞定它!
定义一个由10的幂升序组成的无穷序列。这个序列的开头是:110100100010000…… 输入K(K<231),表示序列中的位置,请你找出在这个无穷序列中K位置上的数字。
经观察,1的位置分别为1,(+1)2,(+2)4,(+3)7,(+4)11,(+5)16,(+6)22,(+7)29,……假设第K个1在第N位,则满足
即
。若K为整数,那么N必定是1,反之,若K为小数,则N为0。当然,在计算K值之前,先要判断
是否大于0。
var
i,k:longint;
temp:real;
begin
readln(k);
if 2.0*k-1.75>0 then
begin
temp:=0.5+sqrt(2.0*k-1.75);
if (abs(trunc(temp)-temp)<1e-8) then writeln(1)
else writeln(0);
end
else writeln(0);
end.
二、把题目转化成数学模型来解决
有的题目不是以数学题本身呈现的,所以我们就要将题目转化成数学模型来解决,也就是通常所说的建模。
有一个由n*n*n个1*1*1的小立方体馅饼组成的大立方体馅饼。有一个虫子从坐标x1,y1,z1出发开始吃馅饼,它只能向与它所在的小立方体相邻的小立方体馅饼前进,比如说当前虫子的坐标是1,1,1,它能到达的坐标是1,1,2和1,2,1和2,1,1。当虫子还没有吃完所有的馅饼时,它都会继续前进。但是它不能到达它所经过了的小立方体。当虫子吃完所有的馅饼时,它会在一个结束的位置x2,y2,z2。现在给定立方体大小n的和起始位置x1,y1,z1还有结束位置x2,y2,z2的值,问是否虫子有可能从起始位置开始吃馅饼,当它吃完所有的馅饼之后到达结束位置? 输入n表示立方体的大小,x1,y1,z1表示起始位置,x2,y2,z2表示结束位置。如果可以输出“Yes”否则输出“No”。
我们来用染色的方法。将每个小立方体染色(红色和蓝色),使得任意相邻的两个小立方体的颜色都相反。可以发现,当n为奇数时,红立方体和蓝立方体的个数差1;当n为偶数时,红立方体与蓝立方体的个数相等。所以,当n为奇数时,如果起点立方体的颜色与终点立方体的颜色不同,则有解;当n为偶数时,如果起点立方体的颜色与终点立方体的颜色相同,则有解。我们就很轻松地把题目转化成了判断x1+y1+z1与x2+y2+z2的奇偶性是否相同的问题。
三、利用数学思想优化算法
有些题目大家可能会选择枚举、搜索等时间复杂度较高的算法,而恰当地加入数学思想可以优化算法,从而降低时间复杂度。
有一个n+2项的数列a[0], a[1],……,a[n+1] (n<=3000,-1000<=ai<=1000)。已知a[i]=(a[i-1] + a[i+1])/2 - c[i](i=1, 2,……,n)。已知 a[0], a[n+1], c[1],……,c[n]。计算a[1]
这道题同学们可能会选择使用递归算法,但我们可以看到n最大为3000,显然时间复杂度太高。其实,我们可以用数学方法来解决这道题。
a[0] + a[2] – 2a[1] – 2c[1] = 0
a[1] + a[3] – 2a[2] – 2c[2] = 0
a[2] + a[4] – 2a[3] – 2c[3] = 0
……
a[n-1] + a[n+1] – 2a[n] – 2c[n] = 0
让我们把上面的式子相加,可以得到
a[0] + a[n+1] – a[1] – a[n] – 2c[1] – 2c[2] – … – 2c[n] = 0
根据a[n-1] + a[n+1] – 2a[n] – 2c[n] = 0 可以得出 a[n+1] – 2c[n] – a[n] = a[n] – 2c[n] – a[n-1]
代入可得a[0] + a[n] – a[1] – a[n-1] – 2c[1] – 2c[2] – … –2c[n-1] = 0
同理:a[0] + a[n-1] – a[1] – a[n-2] – 2c[1] – 2c[2] – … – 2c[n-2] = 0
……
a[0] + a[2] – a[1] – a[1] – 2c[1] = 0
相加上面各式可得
n×a[0] + a[n+1] – (n+1)×a[1] – 2×n×c[1] – 2×(n-1) ×c[2] – … – 2×c[n] = 0
即a[1] =
运用了数学方法,算法的时间复杂度大大降低
const
MaxN=3000;
var
c:array [1..MaxN] of real;
a1,total,a0,an1:real;
n,i:longint;
begin
read(n);
read(a0,an1);
for i:=1 to n do
begin
read(c[i]);
total:=total+c[i]*(n-i+1);
end;
a1:=(n*a0+an1-2*total)/(n+1);
writeln(a1:0:2);
end.
定义f[n]为n的各个数字的乘积,如f[12]=1*2=2,f[1230]=1*2*3*0=0,如果f[n]和f[n+1]都不为0,并且n能被f[n]整除,n+1能被f[n+1]整除,就叫n为perfect数。求k位的perfect数的个数。
枚举所有k位数并判断是大部分同学都可以想到的算法。在k很小的时候,这种枚举算法的劣势不会很明显。但如果k很大10,100,1000……不仅要改用高精度,时间复杂度也高的惊人。数学思想成为了我们改进算法的最有效途径。
我们可以设n=
,
能被f(
)整除,
(
+1≤9,否则
+1=10 f(
)=0)能被f(
)整除
设
=s
=t
(
+1),其中s,t是正整数
s
…
+1=t
…
(
+1)
s
…
+1=t
…
+t
…

…
(t
+t-s
)=1
=
=…=
=1
下面分析
取值(
=1,2,3,4,5,6,7,8逐个分析)
=1对所有k成立
=2,5 对k=3m+1成立(m∈
)
所以:
当k=1时 n=1,2,3,4,5,6,7,8
当k=2时 n=11
当k=3m时 n=
1个
当k=3m+1时 n=
,
,
3个
当k=3m+2时 n=
1个
设有一个N*M方格的棋盘( 1<= N<= 100,1<= M<= 100)。 求出该棋盘中包含有多少个正方形、多少个长方形(不包括正方形)。输入整数N、M,输出正方形的个数和长方型的个数。
枚举长方形(正方形)的顶点可以算出长方形和正方行的个数。但与前面的题目一样,这种算法同样存在着时间复杂度太高的弊端。

如图,中间的红矩形由线段AIAJ,BSBT唯一确定。不妨设m<=n,则共有C(n+1,2)*C(m+1,2)个矩形,其中正方形有:边长为1的有m*n个;边长为2的有(m-1)(n-1)个;……;边长为m的有1*(n-m-1)个,所以有mn+(m-1)(n-1)+…+1*(n-m+1)=
=
个正方形。
同理,有
个长方形。
var
t,i,m,n:longint;
begin
readln(m,n);
write((3*m*m*n-m*m*m+3*m*n+m) div 6,' ');
writeln((3*m*m*n*n-3*m*m*n+3*m*n*n+2*m*m*m-3*m*n-2*m) div 12);
end.
数学思想在程序设计中有着很广泛的应用。我们应该不断地学习,不断地创新,让自己长出一双数学思想的翅膀,在程序设计的天空中飞得更高!