数据结构:组装齿轮问题

题目:


背景

你的弟弟买了一套新的玩具,用这套玩具,他可以组装自己的机器。在玩具中有很多不同尺寸的齿轮,开始的时候他可以直接使用齿轮组装不同转速比

的传动装置,但后来他发现有些转速比用已有的齿轮就很难组装出来。他希望你给他编写一个电脑程序,帮助他找到组装传动装置的方法。

譬如,在这个玩具中包含了 6 齿、12 齿和 30 齿的齿轮,而你的弟弟希望搞一个转速比为 5:4 的传动装置。下图就显示了一种可能的方案:

齿轮

这个传动方案使用了四个齿轮,第一跟轴上是 30 齿和 12 齿的,第二跟轴上是 6 齿和 12 齿的。转速比可以通过如下公式获得:

(30 / 12) * (6 / 12) = (5 / 2) * (1 / 2) = 5 / 4 = 5:4

然而,使用以上三种齿轮,就没法组装出转速比为 1:6 的传动装置。

题目

给定齿轮的大小(齿轮有多少个齿),判断通过这些齿轮能否组成一定的转速比。我们假定每种齿轮的数量都足够多。

输入

输入的第一行是一个数字 n,它表示在玩具中有几种齿轮(1 <= n <= 20)。下一行包含了 n 个数字 c1...cn,以空白符隔开,他们表示了玩具中的

 n 种齿轮的大小(5 <= ci <= 100,其中 1 <= i <= n)。你可以假定在玩具中所有齿轮的大小都是最小齿轮大小的倍数。

再下一行有一个整数 m,它表示所需的实现的转速比有多少组,而之后的 m 行中每行都有两个整数 a 和 b,它们表示要实现的转速比为 a:b,其中

 1 <= a, b <= 10000。

  测试输入关于“测试输入”的帮助 期待的输出关于“期待的输出”的帮助 时间限制关于“时间限制”的帮助 内存限制关于“内存限制”的帮助 额外进程关于“{$a} 个额外进程”的帮助
测试用例 1 以文本方式显示
  1. 3↵
  2. 6 12 30↵
  3. 2↵
  4. 5 4↵
  5. 1 6↵
以文本方式显示
  1. Gear ratio 5:4 can be realized.↵
  2. Gear ratio 1:6 cannot be realized.↵
1秒 1024KB 0
测试用例 2 以文本方式显示
  1. 1↵
  2. 42↵
  3. 2↵
  4. 13 13↵
  5. 42 1↵
以文本方式显示
  1. Gear ratio 13:13 can be realized.↵
  2. Gear ratio 42:1 cannot be realized.↵
1秒 1024KB 0

思路:

这道题的本质其实是利用现有的齿轮齿数与最小齿数的比值,来作为因子,通过乘法或者除法,最后得到目标的比值的分子/分母。一般的做法就是把简化后的齿数相乘,通过大量的得到乘积,同时对目标比例的分子分母同时扩大n倍(分子分母扩大相同的倍数比值不变),测试扩大后的分子分母,是否在之前得到的乘积中出现过。
伪代码:
define num 100000

用每个数除以最小齿数(每个齿轮齿数都是最小齿数的整倍数),得到结果加到a[num]中,得到k个数。

同时用b[num]记录a[num]中出现的数。x出现过则b[x]置为1

//这一步可以先排序,再用后面的数除以第一个,也可以直接让每个数两两相除,如果余数是0而且商不为0,再加到a[num]中

for i 0到k // k是指当前a[num]中的数的个数,在下面的循环中也会改变k值

for j 0到k

if a[j]=0

continue //跳出这次循环,但是要继续

if a[i]%a[j]=0 //一开始我怀疑这个if 存在的意义,但是去掉这个if后最后一个用例无法通过

temp=a[i]/a[j]

if temp=0

continue //temp是0就没有意义了

if b[temp]不为1

temp加入a[num] //同时k++

b[temp]置为1

temp=a[i]*a[j] //temp如果是int型似乎会溢出

if temp>100000或者temp=0 //这个题目的测试用例是有限的,否则这个算法就是大失败

continue

else

if b[temp]不为1

temp加入a[num] //k++

b[temp]置为1

//完毕

看到上面的代码就是这个程序的核心部分——乘法。有了这个步骤以后,就可以在目标比例的分子分母中同时扩大n(n>1)倍时,测试在b[num]中是否为1(当然只要测试到100000)

代码:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#define N 100000
/*...........................
算法分析:
1、首先遍历所有齿轮,求出齿轮与最小齿轮之间的倍数,并用一个数组record记录是否出现过这个倍数
2、倍数之间如果出现了新的倍数关系,加进去
3、进行乘法关系,乘法得到的结果小于一给定值
4、进行匹配,看分子分母是否出现过5、BIT的同学们请不要直接Copy代码,否则后果不堪设想~~~~
.............................*/

static unsigned long number[N];  //对倍数的储存
int mark[N];   //记录是否出现过

int cmp(const void *a,const void *b)
{
	return *(long *)a- *(long *)b;
}

void Init()  //初始化
{
	int k=0;
	memset(number,0,sizeof(number));
	memset(mark,0,sizeof(mark));
	int n;  //齿轮
	static unsigned long temp[30];
	scanf("%d",&n);
	int i,j,min=50000;
	for(i=0;i<n;i++)
	{
		scanf("%lu",&temp[i]);
		if(temp[i]<min) min=temp[i];   //找最小值
	}
	qsort(temp,n,sizeof(temp[0]),cmp);
	for(i=0;i<n;i++)
	{
		int flag=temp[i]/min;
		number[k++]=flag;   //与最小倍数的关系
		mark[flag-1]=1;   //做了标记
	}
	for(i=0;i<k;i++)
	{
		for(j=0;j<k;j++)
		{
			if(number[j]==0) continue;
			if(number[i]%number[j]==0)
			{
				unsigned long tep = number[i]/number[j];
				if(tep==0) continue;  //没有意义了
				if(mark[tep-1] != 1)  //增加新的倍数关系
				{
					number[k++] = tep;
					mark[tep-1]=1;
				}
			}
			unsigned long tep= number[i]*number[j];
			if(tep > N||tep == 0)  continue;//超出和为零没意义
			else
			{
				if(mark[tep-1]!=1)
				{					
					number[k++]=tep;
					mark[tep-1]=1;
				}
			}
		}
	}
	return;
}

long GCD(long a, long b)   //求最大公约数,化简,以免错过倍数
{
	long temp;
	temp=a>b?a:b;
	b=a<b?a:b;
	a=temp;
	temp=a%b;
	while(temp)
	{
		a=b;
		b=temp;
		temp=a%b;
	}
	return b;
}

int main()
{
	freopen("C:\\Users\\Seayar\\Desktop\\input.txt","r",stdin);
	Init();
	int set;
	scanf("%d",&set);
	while(set--)
	{
		long mya,myb,a,b,temp,tep_a,tep_b;
		scanf("%ld%ld",&mya,&myb);
		a=mya;b=myb;
		temp=GCD(a,b);
		a/=temp;
		b/=temp;
		long n=1;  //倍数扩大
		int flag=0;   //标记
		while(1)
		{
			tep_a=a*n;
			tep_b=b*n;
			if(tep_a>N||tep_b>N) break;
			if(mark[tep_a-1]==1&&mark[tep_b-1]==1)
			{
				flag=1;
				break;
			}
			n++;
		}
		if(flag==0)  
			printf("Gear ratio %ld:%ld cannot be realized.\n",mya,myb);  
		else  
			printf("Gear ratio %ld:%ld can be realized.\n",mya,myb);  
	}
	return 0;
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值