lcm:
=a;
whilelcmmodb>0doinc(lcm,a);
end;
(3)素数的求法
A.小范围内判断一个数是否为质数:
functionprime(n:
integer):
Boolean;
varI:
integer;
begin
forI:
=2totrunc(sqrt(n))do
ifnmodI=0thenbegin
prime:
=false;exit;
end;
prime:
=true;
end;
B.判断longint范围内的数是否为素数(包含求50000以内的素数表):
var
s:
array[1..maxn]ofboolean;
begin
fillchar(s,sizeof(s),true);
s[1]:
=false;
fori:
=2tomaxndo
ifs[i]then
begin
j:
=i+i;
whilej<=maxndo
begin
s[j]:
=false;
j:
=j+i;
end;
end;
end.
(4)判断因数的个数
varn,nums,k,i:
longint;
begin
k:
=trunc(sqrt(n));
nums:
=2;
fori:
=2tokdo
ifnmodi=0thennums:
=nums+2;
ifn=sqr(k)thendec(nums);
writeln('nums:
',nums);
end.
(5)分解质因数:
k<=maxlongint;
procedureex3(k:
longint);
var
i,j,t:
longint;
s,u:
array[1..50000]oflongint;
begin
fillchar(s,sizeof(s),0);
fillchar(u,sizeof(u),0);
ex2;//筛出50000以内素数、假设一共L个,存于p数组
t:
=k;
j:
=0;
fori:
=1toLdo
begin
iftmodp[i]=0then
begin
inc(j);
s[j]:
=p[i];
end;
whiletmodp[i]=0do
begin
inc(u[j]);
t:
=tdivp[i];
end;
ift=1thenbreak;
end;
ift<>1then
begin
inc(j);
s[j]:
=t;
u[j]:
=1;{大于50000的因子如果它不是素数一定会在前面的过程中被分解,
end;所以剩下的一定是素数}
end;
(6)欧拉函数(求小于等于N的与N互质的数):
Euler函数通式:
φ(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…..(1-1/pn),其中p1,p2……pn为x的所有质因数,x是不为0的整数。
φ
(1)=1(唯一和1互质的数就是1本身)。
若n是质数p的k次幂,φ(n)=p^k-p^(k-1)=(p-1)p^(k-1),因为除了p的倍数外,其他数都跟n互质。
欧拉函数是积性函数——若m,n互质,φ(mn)=φ(m)φ(n)。
特殊性质:
当n为奇数时,φ(2n)=φ(n),证明与上述类似。
functionex4(n:
longint):
longint;
var
i,j,k:
longint;
begin
ex2;
ex3(n);
j:
=ex3中的j;
ex4:
=1;
fori:
=1tojdo
begin
ex4:
=ex4*(s[j]-1);
fork:
=1tou[j]-1do
ex4:
=ex4*s[j];
end;
end;
(7)求一个数的约数个数:
functionex5(n:
longint):
longint;
var
i,j,k:
longint;
begin
ex2;
ex3(n);
j:
=ex3中的j;
ex5:
=1;
fori:
=1tojdo
ex5:
=ex5*(u[i]+1);
ex5:
=ex5-1;
end;
(8)快速幂、取模:
求a^pmodt
functionex6(a,p,t:
longint):
longint;
var
i,j,k,q:
longint;
begin
q:
=p;
k:
=a;
ex6:
=1;
whileq>0do
begin
ifqand1>0then
ex6:
=ex6*kmodt;
k:
=k*kmodt;
q:
=qshr1;
end;
end;
(9).快速幂
functionf(a,b,n):
longint;
var
t,y:
longint;
begin
t=1;y=a;
whileb<>0do
begin
if(band1)=1thent:
=t*ymodn;
y:
=y*ymodn;
b:
=bshr1;
end;
exit(t);
end;
原理:
把b转换成2进制数
该2进制数第i位的权为a^2^(i-1)
例如
a^11=a^(2^0+2^1+2^3)
(6)高精快速幂
高精度快速幂
//以麦森数为例,麦森数位数计算方法trunc(ln
(2)/ln(10)*n)+1
programmaisen(求2^n-1最后500位);
procedurework(n:
longint);//高精度快速幂主过程
var
i,j:
integer;
begin
ifn=1thenyy[1]:
=2
else
begin
work(ndiv2);
fori:
=1to500do
s[i]:
=0;
fori:
=1to500do
forj:
=1to500do
s[i+j-1]:
=s[i+j-1]+yy[i]*yy[j];
ifnmod2=1then
fori:
=1to500do
s[i]:
=s[i]*2;
fori:
=1to500do
begin
s[i+1]:
=s[i+1]+s[i]div10;
s[i]:
=s[i]mod10;
end;
yy:
=s;
end;
end;
(7).几种常见题型
A.筛法求质因数
var
s:
array[1..maxn]ofboolean;
begin
fillchar(s,sizeof(s),true);
s[1]:
=false;
fori:
=2tomaxndo
ifs[i]then
begin
j:
=i+i;
whilej<=maxndo
begin
s[j]:
=false;
j:
=j+i;
end;
end;//截止到此为筛法求素数
m:
=0;
fori:
=2tomaxndo
ifs[i]thenbegin
count:
=0;
repeat
r:
=nmods[i];
ifr=0thenbegin
n:
=ndivs[i];
count:
=count+1;
end;
untilr<>0;
ifcount<>0thenbegin
m:
=m+1;a[m]:
=s[i];b[m]:
=count;
end;
end;
end.
B.欧拉函数(求小于n的且与n互质的数的个数)
//上接筛法求质因数:
即已求得n的各素因数
a:
=n;
fori:
=1tomdo
a:
=a*(a[m]-1);
b:
=1;
fori:
=1tomdo
b:
=b*a[m];
ans:
=adivb;
C.数的拆分
数的所有拆分方法
vart,n:
integer;
a,b:
array[1..1000]ofinteger;
proceduretry(m,start,k:
integer);
vari,j:
integer;
begin
fori:
=startto(mdiv2)do{优化了一下,因为不可能到达N,拆分了只会到达这个数的一半}
begin
a[k]:
=m-i;{减去拆出来的这个数}
t:
=t+1;{有了一种情况,计数}
try(a[k],i,k+1);{在再拆过的这个数上再拆,直到不能拆为止}
end;
end;
begin
readln(n);
try(n,1,1);
writeln(t);
end.
如果把结果打出来是这样的,其实每个人的方法都不一样
N=7
7=1+6
7=1+1+5
7=1+1+1+4
7=1+1+1+1+3
7=1+1+1+1+1+2
7=1+1+1+1+1+1+1
7=1+1+1+2+2
7=1+1+2+3
7=1+2+4
7=1+2+2+2
7=1+3+3
7=2+5
7=2+2+3
7=3+4
D.非零自然数的倒数分解问题
1/16=1/48+1/241/18=1/54+1/27 1/24=1/72+1/36 1/19=1/380+1/20
由于每个倒数分解后第分数的分子都是1,因此只需要考虑第二个分数能不能通过约分使分子为1。
这些分数的分子都是2,明显能分别与16、18、24约分得1。
换句话说,16、18、24都含有因数2。
更进一步说,就是只要分解出来的分数分子是分母的因数,就可实现把这个分数写成某个自然数的倒数的形式。
思考:
要对这些倒数进行分解,首先应搞清楚分母有哪些因数,也就是要对分母分解质因数,进而找出其包含的因数。
比如:
16=2×2×2×2。
它的因数有2、4、8、16四个,那么1/16的分子分母 同乘3、5、9或17都能转换成分子为1的两个分数和的形式。
例如19是质数,只有1和19两个因数,于是1/19=20/(19×20)=1/380+1/20。
归纳上述解题过程,我们得出自然数倒数分解的几个步骤:
1、找出分母的某个因数m;2、分子分母同乘m+1;3、把新得到的分数分解成两个分数的和;4、对分子为m的分数进行约分
上述过程可以用代数的形式表示:
设m是非零自然数p的一个因数,那么一定存在另一因数n,使得p=mn,从而1/p=(m+1)/p(m+1)=1/(p(m+1)+m/p(m+1)=1/p(m+1)+1/n(m+1)。
当m=1时,n=p,由上式知1/p=1/2p+1/2p,不符合要求。
因此在没有特别说明的情况下,以后所讲因数均不含1。
既然m、n都是p的因数,那么把1/p分解成分子分别是m和n的两个分数,然后约分也可得到符合条件的分解。
1/p=(m+n)/p(m+n)=m/(p(m+n)+n/p(m+n)=1/n(m+n)+1/m(m+n)。
至些,我们发现,只要m、n是p的因数,即便p≠mn,上式仍然成立。
结论:
①任何大于1的自然数的倒数都可以分解成其他两具不同自然数的倒数和的形式。
②将非零自然的倒数分解成其他两个不同自然数的倒数和,其方法的多少是由这个自然数的因数的个数决定的。
③推论:
任何非零自然的倒数可以表示为任意多个其他自然数的倒数和的形式。
E.高精运算
【题目】数学上定义:
n!
=1×2×3×...×(n-1)×n(N>0)
0!
=1
若用integer型数据表示阶乘,最多可到7!
用Longint类型也只能到12!
要求输入正整数n,求n!
的精确表示
【算法分析】用数组存放结果,模拟人工计算过程,逐位去乘,注意进位情况的处理。
vara:
array[1..10000]ofinteger;
b,c,d,t,x:
integer;
begin
write('inputnumber:
');
readln(x);
if(x<0)thenbeginwriteln('error!
');readln;halt;end;
fort:
=1to10000doa[t]:
=0;
d:
=1;a[1]:
=1;
forc:
=1toxdo{一直乘到x}
begin
t:
=1;b:
=0;{t:
第几位数b:
进位d:
总位数}
repeat
a[t]:
=a[t]*c+b;{数组每位均乘上c,同时加上进位}
b:
=a[t]div10;{分离出进位}
ifa[t]>=10thenif(t=d)thend:
=d+1;{假如最后一位乘时有}
{进位,则总位数加1}
a[t]:
=a[t]mod10;
inc(t);{数组下一位}
until(t>d);{直到乘完数组的每一位数字}
end;
write(x,'!
=');
fort:
=ddownto1dowrite(a[t]);{输出}
end.
F.万年历问题
【题目】输入年、月、日,求这一天是星期几。
【参考程序1】
【算法提要】求出这一天离公元1年的元旦有多少天days,然后对7求余
const
first=1;{公元1年为基准}
first_week=1;{公元1年的元旦为星期一}
yue:
array[1..12]of1..31=(31,28,31,30,31,30,31,31,30,31,30,31);
week_:
array[0..6]ofstring[20]=('Sunday','Monday','Tuesday','Wedsday',
'Thursday','Friday','Saturday');
var
days,week,year,month,date,i,years:
longint;
begin
writeln('year:
');readln(year);
writeln('month');readln(month);
writeln('date');readln(date);
years:
=0;days:
=0;
fori:
=firsttoyear-1do
if(imod400=0)or(imod4=0)and(imod100<>0)then
beginyears:
=years+1;end;{注意处理闰年的情况}
days:
=(year-first)*365+years;{离基准年过了多少天}
fori:
=1tomonth-1dodays:
=days+yue[i];{本年过了多少个月}
fori:
=1todatedodays:
=days+1;{本月过了多少天}
if((yearmod400=0)or(yearmod4=0)and(yearmod100<>0))
and(month>3)thendays:
=days+1;{如果本年为闰年,且月份超2月,还要考虑加1}
week:
=((days-1)mod7+first_week)mod7;{求星期数}
writeln('itis',week_[week]);
readln;
end.
【参考程序2】用公式法:
days:
=trunc((year-1)*(1+1/4-1/100+1/400)+c)
用求出的days对7求余数。
其中c为该天离该年元旦的天数
const
first=1;
first_week=1;
yue:
array[1..12]of1..31=(31,28,31,30,31,30,31,31,30,31,30,31);
week_:
array[0..6]ofstring[20]=('Sunday','Monday','Tuesday','Wedsday',
'Thursday','Friday','Saturday');
var
days,week,year,month,date,i:
longint;
begin
writeln('year:
');readln(year);
writeln('month');readln(month);
writeln('date');readln(date);
days:
=0;
fori:
=1tomonth-1dodays:
=days+yue[i];
fori:
=1todatedodays:
=days+1;
if((yearmod400=0)or(yearmod4=0)and(yearmod100<>0))
and(month>3)thendays:
=days+1;
days:
=trunc((year-1)*(1+1/4-1/100+1/400)+days);
week:
=daysmod7;
writeln('itis',week_[week]);
readln;
end.
2.求最小生成树
见课本
3.最短路径
见课本
4.0-1背包问题(部分背包问题可有贪心法求解:
计算Pi/Wi)
数据结构:
w[i]:
第i个背包的重量;
p[i]:
第i个背包的价值;
(1)0-1背包:
每个背包只能使用一次或有限次(可转化为一次):
A.求最多可放入的重量。
NOIP2001装箱问题
有一个箱子容量为v(正整数,o≤v≤20000),同时有n个物品(o≤n≤30),每个物品有一个体积(正整数)。
要求从n个物品中,任取若千个装入箱内,使箱子的剩余空间为最小。
多重背包:
procedureMultiplePack(cost,weight,amount)
ifcost*amount>=V
CompletePack(cost,weight)
return
integerk=1
whilekZeroOnePack(k*cost,k*weight)
amount=amount-k
k=k*2
ZeroOnePack(amount*cost,amount*weight)
l搜索方法
proceduresearch(k,v:
integer);{搜索第k个物品,剩余空间为v}
vari,j:
integer;
begin
ifv=v;
ifv-(s[n]-s[k-1])>=bestthenexit;{s[n]为前n个物品的重量和}
ifk<=nthenbegin
ifv>w[k]thensearch(k+1,v-w[k]);
search(k+1,v);
end;
end;
lDP
F[I,j]为前i个物品中选择若干个放入使其体积正好为j的标志,为布尔。
实现:
将最优化问题转化为判定性问题
F[I,j]=f[i-1,j-w[i]](w[I]<=j<=v)边界:
f[0,0]:
=true.
ForI:
=1tondo
Forj:
=w[I]tovdoF[I,j]:
=f[I-1,j-w[I]];
优化:
当前状态只与前一阶段状态有关,可降至一维。
F[0]:
=true;
ForI:
=1tondobegin
F1:
=f;
Forj:
=w[I]tovdo
Iff[j-w[I]]thenf1[j]:
=true;
F:
=f1;
End;
B.求可以放入的最大价值。
F[I,j]=
C.求恰好装满的情况数。
(2)每个背包可使用任意次:
A.求最多可放入的重量。
状态转移方程为
f[I,j]=max{f[i-w[j]
B.求可以放入的最大价值。
USACO1.2ScoreInflation
进行一次竞赛,总时间T固定,有若干种可选择的题目,每种题目可选入的数量不限,每种题目有一个ti(解答此题所需的时间)和一个si(解答此题所得的分数),现要选择若干题目,使解这些题的总时间在T以内的前提下,所得的总分最大,求最大的得分。
*易想到:
f[i,j]=max{f[i-k*w[j],j-1]+k*v[j]}(0<=k<=idivw[j])
其中f[i,j]表示容量为i时取前j种背包所能达到的最大值。
*优化:
Begin
FillChar(problem,SizeOf(problem),0);
Assign(Input,'inflate.in');
Reset(Input);
Readln(M,N);
Fori:
=1ToNDo
Withproblem[i]Do
Readln(point,time);
Close(Input);
FillChar(f,SizeOf(f),0);
Fori:
=1ToMDo
Forj:
=1ToNDo
Ifi-problem[j].time>=0Then
Begin
t:
=problem[j].point+f[i-problem[j].time];
Ift>f[i]Thenf[i]:
=t;
End;
Assign(Output,'inflate.out');
Rewrite(Output);
Writeln(f[M]);
Close(Output);
End.
C.求恰好装满的情况数。
AhOI2001Problem2
求自然数n本质不同的质数和的表达式的数目。
思路一,生成每个质数的系数的排列,在一一测试,这是通法。
proceduretry(dep:
integer);
vari