有一年代尚无考究的古尺长29寸,因磨损日久尺上的刻度只剩下7条,其余刻度均已不复存在,神奇的是,使用该尺仍可一次性度量1~29之间任意整数寸长度;
设计程序,试确定古尺上7条刻度的位置分布;
7刻度度量29尺长
1.说明:
这是一道有一定难度的实用性较强的趣题;
要使长为29(单位略)的直尺一次性度量1~29之间的任意整数长(简称完全度量),少于7条刻度是不行的;
事实上,假若只有6条刻度,连同尺的两条端线共8条,8取2的组合数为28,即6条刻度的直尺最多只有28种度量长度,显然小于29;
为了寻求实现直尺完全度量的7条刻度的分布位置,设置数组a(8)和b(36),尺左端为a(0)=0,a(i)(i=2,……,7)在2~s-1中取不重复的数,不妨设:
- 2<=a(2)< a(3)<……< a(7)<=s-1
设置a(2),a(3),……,a(7)循环: a(2)取2开始,以后a(i)从a(i-1)+1开始递增1取值,直至s-(8-i)为止,这样可以避免重复取值;
当i=7时,7条刻度连同尺的两条端线共9条,组合数为36,36种长度赋给b数组元素b(1),b(2),b(3),……,b(36);
为判定某种刻度分布位置能否实现完全度量,设置特征量u:对于1<=d<=s的每一个长度d,如果在b(1)~b(36)中存在某一元素等于d,特征量u值增1,最后,若u=29,说明从1至尺长29的每一个整数d都有一个b(i)相对应,即实现完全度量,打印直尺的段长序列;
2.程序设计:
#include<stdio.h>
int main()
{
int c,d,j,k,s,t,u,a[9],b[37];
printf("7刻度分29尺长为8段,段长序列为:\n");
s=29;
a[0]=0;
a[1]=1;
a[8]=s;
c=0;
for(a[2]=2;a[2]<=s-6;a[2]++)
for(a[3]=a[2]+1;a[3]<=s-5;a[3]++)
for(a[4]=a[3]+1;a[4]<=s-4;a[4]++)
for(a[5]=a[4]+1;a[5]<=s-3;a[5]++)
for(a[6]=a[5]+1;a[6]<=s-2;a[6]++)
for(a[7]=a[6]+1;a[7]<=s-1;a[7]++)
{
for(t=0,k=0;k<=7;k++)
for(j=k+1;j<=8;j++)
{
t++; /*序列部分和赋值给b数组*/
b[t]=a[j]-a[k];
}
for(u=0,d=1;d<=s;d++)
for(k=1;k<=36;k++)
if(b[k]==d) /*检验b数组取1~s有多少个*/
{
u+=1;
k=36;
}
if(u==s) /*b数组值包括1~s所有整数*/
{
if((a[7]!=s-1) || (a[7]==s-1) && (a[2]<=s-a[6]))
{
c++;
printf("NO%d:",c); /*输出解的段长序列*/
for(k=1;k<=7;k++)
printf("%2d,",a[k]-a[k-1]);
printf("%2d \n",s-a[7]);
}
}
}
if(c>0)
printf("共有以上%d个解\n",c); /*输出解的个数*/
}
3.程序运行示例及其注意事项:
7刻度分29尺长为8段,段长序列为:
NO1: 1, 1,12, 4, 3, 3, 3, 2
NO2: 1, 2, 3, 7, 7, 4, 4, 1
NO3: 1, 3, 6, 6, 6, 2, 3, 2
共有以上3个解
注意:
为便于验证,直尺的刻度分布采用段长序列即相邻两刻度之间的相距间隔来输出,对于7刻度而言,尺长29有以上3个解,即题目探求古尺刻度分布;
以上输出结果是否能“完全度量”,不妨以第2个解予以展示,度量值为相连的若干段长之和,从小到大依次为:1,2,3,4,2+3,1+2+3,7,4+4,4+4+1,3+7,7+4,2+3+7,1+2+3+7,7+7,7+4+4,7+4+4+1,3+7+7,7+7+4,2+3+7+7,1+2+3+7+7,3+7+7+4,7+7+4+4,7+7+4+4+1,1+2+3+7+7+4,3+7+7+4+4,3+7+7+4+4+1,2+3+7+7+4+4,1+2+3+7+7+4+4,1+2+3+7+7+4+4+1=29;
可见,7刻度可完全度量29尺长;
拓广到n条刻度s尺长
一般地,探索尺长为s、刻度数为n(s、n均为正整数)的完全度量问题上;
1.说明:
为了寻求实现尺长s完全度量的n条刻度的分布位置,设置以下两个数组:数组a元素a(i)为第i条刻度距离尺左端线的长度,约定a(0)=0和a(n+1)=s对应尺的左右端线,注意到尺的两端至少有一条刻度距端线为1(否则长度s-1不能度量),不妨设a(1)=1,其余的a(i)(i=2,……,n)在2~s-1中取数,不妨设:
- 2<=a(2)< a(3)<……< a(n)<=s-1
从a(2)取2开始,以后a(i)从a(i-1)+1开始递增1取值,直至s-(n+1)+i为止;
当i=n时,n条刻度连同尺的两条端线共n+2条,从n+2取2的组合数为C(n+2,2),记为m,显然有:
- m=C(n+2,2)=(n+1)(n+2)/2
试把m个长度赋给b数组元素b(1),b(2),……,b(m),为判定某种刻度分布能否实现完全度量,设置特征量u,对于1<=d<=s的每一个长度d,如果在b(1)~b(m)中存在某一元素等于d,特征量u值增1;
最后,若u=s,说明从1至尺长s的每一个整数d都有一个b(i)相对应,即达到完全度量,于是输出带n条刻度分布的直尺,并打印相应的段长序列;
若i< n,i增1后a[i]=a[i-1]+1后继续探索;
当i>1时a(i)增1继续,至a(i)=s-(n-1)+i时回溯;
**2.程序设计:
#include<stdio.h>
int main()
{
int d,i,j,k,t,u,s,m,n,a[30],b[300];
printf("尺长s,寻求n条刻度分布,请确定s,n:");
scanf("%d,%d",&s,&n);
a[0]=0;
a[1]=1;
a[n+1]=s;
m=(n+2)*(n+1)/2;
i=1;
a[i]=2;
while(1)
{
if(i<n)
{
i++;
a[i]=a[i-1]+1;
continue;
}
else
{
for(t=0,k=0;k<=n;k++)
for(j=k+1;j<=n+1;j++)
{
t++; /*序列部分和赋值给b数组*/
b[t]=a[j]-a[k];
}
for(u=0,d=1;d<=s;d++)
for(k=1;k<=m;k++)
if(b[k]==d) /*检验b数组取1~s有多少个*/
{
u+=1;
k=m;
}
if(u==s)
{
if((a[n]!=s-1) || (a[n]==s-1) && (a[2]<=s-a[n-1]))
{
printf("◤"); /*输出尺额上边*/
for(k=1;k<=s-1;k++)
printf("▔");
printf("◥\n");
printf("▍");
for(k=1;k<=n+1;k++) /*输出尺的数字标注*/
{
for(j=1;j<=a[k]-a[k-1]-1;j++)
printf(" ");
if(k<n+1)
printf("%2d",a[k]);
else
printf("▍\n");
}
printf("◣");
for(k=1;k<=n+1;k++)
{
for(j=1;j<=a[k]-a[k-1]-1;j++)
printf("▔");
if(k<n+1)
printf("╪");
else
printf("◢\n");
}
printf("直尺%d段的段长序列为:",n+1); /*输出段长序列*/
for(k=1;k<=n;k++)
printf("%2d,",a[k]-a[k-1]);
printf("%2d \n",s-a[n]);
}
}
}
while(a[i]==s-(n+1)+i && i>1) /*调整或回溯*/
i--;
if(i>1)
a[i]++;
else
break;
}
}
3.程序运行示例及其注意事项:
尺长s,寻求n条刻度分布,请确定s,n:36,8
▼▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▼
| 1 3 6 13 20 27 31 35 |
▼▔|▔▔|▔▔▔|▔▔▔▔▔▔|▔▔▔▔▔▔|▔▔▔▔▔▔|▔▔▔▔|▔▔▔▔▔▔|▔▔▔▼直尺9段的段长序列为:1,2,3,7,7,7,4,4,1
对应于n=8,输入s=37,38,……,45,都没有输出,说明8条刻度能完成完全度量的长度最大值36;
顺便指出网上某些关于Colomb尺上7刻度能完全度量44尺长、8刻度能完全度量55尺长等结论显然不成立;
思考:
由以上程序得到的8刻度分布的段长序列与前面7刻度分布的第2个解比较,两个解首均为“1,2,3”,尾均为“4,4,1”,首尾完全相同,只是中间相差一个“7”段,由此可以总结出以下一般结论:
对于n(n>6)条刻度把直尺分割为如下分布的n+1段:
- 1,2,3,7,7,……,7,4,4,1
其中尺的中部有连续n-5个“7”段,对应尺长为7n-20,则该尺可实现完全度量;
请证明以上结论;