程序设计竞赛基础实训81.docx
《程序设计竞赛基础实训81.docx》由会员分享,可在线阅读,更多相关《程序设计竞赛基础实训81.docx(13页珍藏版)》请在冰豆网上搜索。
程序设计竞赛基础实训81
2012程序设计竞赛基础实训82
39台球碰撞
在平面直角坐标系下,台球桌是一个左下角在(0,0),右上角在(L,W)的矩形。
有一个球心在(x,y),半径为R的圆形母球放在台球桌上(整个球都在台球桌内)。
受撞击后,球沿极角为b的射线(即:
x正半轴逆时针旋转到此射线的角度为b)以初速度v飞出。
因球与桌面的磨擦,球作的加速度为a的匀减速率运动,每次碰到球桌边框时均发生完全弹性碰撞(反射角等于入射角)。
试求球停止时球心所在位置。
输入:
l=180,w=120,x=35,y=40,r=5,b=30,v=27,a=6
原题:
在平面直角坐标系下,台球桌是一个左下角在(0,0),右上角在(L,W)的矩形。
有一个球心在(x,y),半径为R的圆形母球放在台球桌上(整个球都在台球桌内)。
受撞击后,球沿极角为a的射线(即:
x正半轴逆时针旋转到此射线的角度为a)飞出,每次碰到球桌时均发生完全弹性碰撞(球的速率不变,反射角等于入射角)。
如果球的速率为v,s个时间单位之后球心在什么地方?
输入
输入文件最多包含25组测试数据,每个数据仅一行,包含8个正整数L,W,x,y,R,a,v,s(100<=L,W<=105,1<=R<=5,R<=x<=L-R,R<=y<=W-R,0<=a<360,1<=v,s<=105),含义见题目描述。
L=W=x=y=R=a=v=s=0表示输入结束,你的程序不应当处理这一行。
输出
对于每组数据,输出仅一行,包含两个实数x,y,表明球心坐标为(x,y)。
x和y应四舍五入保留两位小数。
样例输入
样例输出
1001008010590223
1101007010518019999
00000000
80.0056.00
71.0010.00
设计要点:
(1)确定球心区域
设球心座标为(x,y),则有球心矩形区域:
x1≤x≤x2,y1≤y≤y2
其中:
x1=r,x2=l-r;y1=r,y2=w-r
(2)没撞击时球位置
设开始时球心位于(x0,y0),球沿极角为a的射线射出,球的速率为v,s个时间单位之后球心在(x,y):
x=x0+v*s*cos(a*3.14159/180)
y=y0+v*s*sin(a*3.14159/180)
(3)撞击轴对称
若球与右竖边(x2)撞击,撞击后横月座标为:
x=2*x2-x
若球与上横边(y2)撞击,撞击后纵月座标为:
y=2*y2-y
程序设计:
//台球碰撞
#include
#include
voidmain()
{
doublel,w,r,a,v,s,x,y,x1,x2,y1,y2;
printf("请确定球台边框(l,w):
");scanf("%lf,%lf",&l,&w);
printf("请确定球心开始位置(x,y):
");
scanf("%lf,%lf",&x,&y);
printf("请确定球半径r:
");scanf("%lf",&r);
printf("请确定射击角度a:
");scanf("%lf",&a);
printf("请确定射击速度v:
");scanf("%lf",&v);
printf("请确定时间s:
");scanf("%lf",&s);
x1=r;x2=l-r;y1=r;y2=w-r;
x=x+v*s*cos(a*3.1415926/180);
y=y+v*s*sin(a*3.1415926/180);
while(xx2||yy2)
{if(x>x2)x=2*x2-x;
if(xif(y>y2)y=2*y2-y;
if(y}
printf("所求位置为:
(%.2f,%.2f).\n",x,y);
}
请确定球台边框(l,w):
130,110
请确定球心开始位置(x,y):
30,40
请确定球半径r:
5
请确定射击角度a:
30
请确定射击速度v:
20
请确定时间s:
20
所求位置为:
(113.59,40.00).
引申:
在平面直角坐标系下,台球桌是一个左下角在(0,0),右上角在(L,W)的矩形。
有一个球心在(x,y),半径为r的圆形母球放在台球桌上(整个球都在台球桌内)。
受撞击后,球沿极角为b的射线(即:
与正半轴逆时针旋转到此射线的角度为b)以初速度v飞出。
因球与桌面的磨擦,球作的加速度为a的匀减速率运动,每次碰到球桌边框时均发生完全弹性碰撞(反射角等于入射角)。
试求球停止时球心所在位置。
解:
球作的初速度为v0,加速度为a的匀减速率运动,设t时刻球的速度为vt,则
vt=v0-a*t
停止时vt=0,则运行时间为:
t=v0/a
球运行距离为s=v0*t-a*t*t/2=v0*v0/2/a
//台球碰撞
#include
#include
voidmain()
{
doublel,w,r,a,b,v,s,x,y,x1,x2,y1,y2;
printf("请确定球台边框(l,w):
");scanf("%lf,%lf",&l,&w);
printf("请确定球心开始位置(x,y):
");scanf("%lf,%lf",&x,&y);
printf("请确定球半径r:
");scanf("%lf",&r);
printf("请确定射击角度b:
");scanf("%lf",&b);
printf("请确定射击初速度v:
");scanf("%lf",&v);
printf("请确定匀减速的加速度a:
");scanf("%lf",&a);
x1=r;x2=l-r;y1=r;y2=w-r;
s=v*v/2/a;
x=x+s*cos(b*3.1415926/180);
y=y+s*sin(b*3.1415926/180);
while(xx2||yy2)
{if(x>x2)x=2*x2-x;
if(xif(y>y2)y=2*y2-y;
if(y}
printf("球停止时球心所求位置为:
(%.2f,%.2f).\n",x,y);
}
请确定球台边框(l,w):
180,120
请确定球心开始位置(x,y):
35,40
请确定球半径r:
5
请确定射击角度b:
30
请确定射击初速度v:
27
请确定匀减速的加速度a:
6
球停止时球心所求位置为:
(87.61,70.37).
40守形数
若正整数n是它平方数的尾部,则称n为守形数,又称同构数。
例如,6是其平方数36的尾部,76是其平方数5776的尾部,6与76都是守形数。
试求出指定区间[x,y]内所有守形数。
测试数据:
(1)x=10,y=10000,输出:
(2)x=10000,y=1000000,输出:
1.常规求解
(1)设计要点
对指定范围[x,y]内的每一个整数a(约定a>1),求出其平方数s;
计算a的位数w,同时计算b=10^w,a的平方s的尾部c=s%b;
比较a,c,若a=c则输出守形数。
(2)程序实现
//求[x,y]内的守形数
#include
voidmain()
{longinta,b,c,k,s,x,y;
printf("求区间[x,y]中的守形数.");
printf("请输入整数x,y:
");
scanf("%ld,%ld",&x,&y);
for(a=x;a<=y;a++)
{s=a*a;//计算a的平方数s
b=1;k=a;
while(k>0)
{b=b*10;k=k/10;}
c=s%b;//c为a的平方数s的尾部
if(a==c)
printf("%ld^2=%ld\n",a,s);
}
}
(3)程序运行结果
求区间[x,y]中的守形数.请输入整数x,y:
10,10000
25^2=625
76^2=5776
376^2=141376
625^2=390625
9376^2=87909376
2.探索n位守形数
(1)求解要点
为了求更多位数的守形数,可应用守形数的性质:
一个m位守形数的尾部m-1位数也是一个守形数。
道理很简单,a是一个m位数,a的平方数尾部的m-1位仅由a的尾部m-1位决定而与a的其他位无关。
实施易知一位守形数有三个:
1,5,6。
则二位守形数的个位数字只可能是1,5,6这三个数字。
根据这一思路,我们可应用递推求出多位守形数。
(2)程序设计
//求n位守形数
#include
voidmain()
{intn,d,k,j,i,t,m,w,z,u,v,a[500],b[500],c[500];
printf("n=");scanf("%d",&n);
for(d=1;d<=9;d++)
{for(k=1;k<=500;k++)
{a[k]=0;b[k]=0;c[k]=0;}
a[1]=d;//给个位数赋值
for(k=2;k<=n;k++)
{for(j=0;j<=9;j++)
{a[k]=j;v=0;
for(i=1;i<=k;i++)c[i]=0;//探索a(k)
for(i=1;i<=k;i++)
{for(z=0,t=1;t<=k;t++)
{u=a[i]*a[t]+z;z=u/10;
b[i+t-1]=u%10;//计算平方
}
for(w=0,m=i;m<=k;m++)
{u=c[m]+b[m]+w;
w=u/10;c[m]=u%10;
}
}
for(i=1;i<=k;i++)
if(a[i]!
=c[i])v=1;
if(v==0)break;
}
}
if(v==0&&a[n]!
=0)//输出n位守形数结果
{printf("%d结尾的%d守形数:
",a[1],n);
for(k=n;k>=1;k--)
printf("%d",a[k]);
printf("\n");
}
}
}
(3)程序运行示例
运行程序,输入n=30,得30位守形数
5结尾的30守形数:
106619977392256259918212890625
6结尾的30守形数:
893380022607743740081787109376
41奇数序列运算式
在由指定相连奇数组成的序列的每相邻两项中插入运算符号:
若相邻两项都是合数,则两项中插入“-”号;
若相邻两项一项合数一项素数,则两项中插入“+”号;
若相邻两项都是素数,则两项中插入乘号“*”号;
输入奇数b,c(b例如b=31,c=45,完成运算式为:
31+33-35+37+39+41*43+45=1913
测试数据:
(1)b=3,c=51
(2)b=2011,c=2029
//奇数序列运算式
#defineN30000
#include
#include
voidmain()
{intb,c,f,m,n,k,i,j,a[N];longt,s;
printf("请输入首尾奇数b,c(b");scanf("%d,%d",&b,&c);
m=b-2;n=(c-m)/2;//奇数序列2k+m(k=1,2...,n))
for(k=1;k<=n+1;k++)a[k]=0;
for(k=1;k<=n;k++)
{for(t=0,j=3;j<=sqrt(2*k+m);j+=2)
if((2*k+m)%j==0){t=1;break;}
if(t==0)a[k]=1;//标记第k个奇数2k+m为素数
}
printf("\n%d",b);
for(i=2;i<=n;i++)//完成表达式
{if(a[i-1]+a[i]==0)printf("-%d",2*i+m);//插入减号
if(a[i-1]+a[i]==1)printf("+%d",2*i+m);//插入加号
if(a[i-1]+a[i]==2)printf("*%d",2*i+m);//插入乘号
}
s=0;a[0]=1-a[1];a[n+1]=0;
for(i=1;i<=n;i++)//计算表达式结果
{t=2*i+m;f=i;
while(a[i]+a[i+1]==2)
{i++;t=t*(2*i+m);}//相邻项均为素数时相乘
if(a[f-1]+a[f]==0)s=s-t;
if(a[f-1]+a[f]==1)s=s+t;
}
printf("=%d.\n",s);
}
请输入首尾奇数b,c(b3,99
12857
请输入首尾奇数b,c(b2001,2029
4114752
42构建旋转方阵
把整数1,2,...,n2从外层至中心按顺时针方向螺旋排列所成的n×n方阵,称顺转n阶方阵;按逆时针方向螺旋排列所成的称逆转n阶方阵。
1242322212019
2254039383718
3264148473617
4274249463516
5284344453415
6293031323314
78910111213
上图为逆转7阶方阵。
设计程序选择分别打印逆转10阶方阵与顺转15阶方阵。
(1)设计要点
打印二种旋转方阵关键在于数组元素的赋值以及赋值与打印的巧妙结合。
对应方阵的n行n列设置二维数组a(n,n)。
令m=int(n/2),当n为偶数时,方阵共m圈。
当n为奇数时,方阵除m圈外正中间还有一个数a(m+1,m+1)=n*n。
对于m圈,每圈有上下左右四条边。
最外圈定义为第1圈,从外往内依次定义为第2圈,….第i圈每边有n-2i+1个数。
为了实现旋转准确对各圈各边的每一个数组元素赋值,我们引入中间变量s,t:
s=n-2i+1
t=t+4s(t置初值0)
设置i(1─m)循环对第i圈操作,设置j(i─n-i)循环对第i圈的四条边的n-2i+1个元素操作。
i,j二重循环可对方阵的每一元素赋值。
在顺时针转方阵中,具体赋值为:
上行为a(i,j)=t+1-i+j:
其中+j体现往右元素值递增1;+t-i体现随圈数i增加数值增加值;而1为具体调整数。
右列为a(j,n+1-i)=t+s+1+j-i,即在a(i,j)的基础上增s。
下行为a(n+1-i,j+1)=t+3*s-j+i:
其中-j体现往左元素值递增1;+t+i体现随圈数i增加数值增加值;而3*s为具体调整数。
左列为a(j+1,i)=t+4*s-j+i,即在a(n+1-i,j+1)基础上增s。
在逆时针转方阵中,还是上述赋值,只是打印输出时把行列互换。
这样处理是巧妙的,较为简便。
(2)程序实现
//旋转方阵
#include
#include
voidmain()
{inti,j,m,n,t,s,z,a[20][20];
printf("输入方阵阶n:
");
scanf("%d",&n);
printf("方阵有以下两种旋转方式:
\n");
printf("1:
逆时针转2:
顺时针转\n");
printf("选择旋转方式代码:
");scanf("%d",&z);
m=n/2;t=0;
a[m+1][m+1]=n*n;
for(i=1;i<=m;i++)//按规律给a数组赋值
{s=n+1-2*i;
for(j=i;j<=n-i;j++)
{a[i][j]=t+1-i+j;
a[j][n+1-i]=t+s+1+j-i;
a[n+1-i][j+1]=t+3*s-j+i;
a[j+1][i]=t+4*s-j+i;
}
t=t+4*s;
}
printf("所求旋转方阵为:
");
for(i=1;i<=n;i++)
{printf("\n");
for(j=1;j<=n;j++)//按座标输出方阵
if(z%2==0)
printf("%4d",a[i][j]);
else
printf("%4d",a[j][i]);
}
}
(3)程序运行示例与变通
输入方阵阶n:
7
方阵有以下两种旋转方式:
1:
逆时针转2:
顺时针转
选择旋转方式代码:
1
所求旋转方阵为:
1242322212019
2254039383718
3264148473617
4274249463516
5284344453415
6293031323314
78910111213
输入方阵阶n:
8
方阵有以下两种旋转方式:
1:
逆时针转2:
顺时针转
选择旋转方式代码:
2
所求旋转方阵为:
12345678
282930313233349
2748495051523510
2647606162533611
2546596463543712
2445585756553813
2344434241403914
2221201918171615
程序变通:
把程序中的输出量a[i][j]改变为n*n-a[i][j]+1,可输出由内到外的旋转方阵。
43n!
精确计算
定义n!
=1*2*3*…*n
输入正整数n(<=100),精确计算并输出n!
(若大于10位时输出其高10位)
测试数据:
n=30
n=100
44分数数列
老师为了检测学生的观察分析能力与程序设计水平,写出一个递推分数数列的前6项:
1/2,3/5,4/7,6/10,8/13,9/15,...,引导学生注意观察数列的构成规律:
第i项的分母d与分子c存在以下关系:
d=c+i.而c为与前i-1项中的所有分子、分母均不相同的最小正整数。
试求出该数列的第n项,并求出前n项中的最大项。
测试数据:
(1)n=1000
(2)n=2012
45双和数组
把一个偶数2s分解为6个互不相等的正整数a,b,c,d,e,f,然后把这6个正整数分成(a,b,c)与(d,e,f)两个组,若这两组数具有以下两个相等特性:
则把数组(a,b,c)与(d,e,f)称为基于s的双和数组(约定a
1)存在双和数组,s至少为多大?
2)当s=98时有多少个不同的双和数组?