OJ1142.丑数问题

题目描述

对于一给定的素数集合 S=P1,P2,...,Pk考虑一个正整数集合,该集合中任一元素的质因数全部属于 S 。这个正整数集合包括,P1​ 、P1×P1 、P1×P2 、P1×P2×P3 ...(还有其它)。该集合被称为 S 集合的“丑数集合”。注意:我们认为 1 不是一个丑数。

你的工作是对于输入的集合 S 去寻找“丑数集合”中的第 n 个“丑数”。

补充:丑数集合中每个数从小到大排列,每个丑数都是素数集合中的数的乘积,第 n 个“丑数”就是在能由素数集合中的数相乘得来的(包括它本身)第 n 小的数。

输入格式

输入的第一行是两个的整数,分别代表集合 S 的大小 k 和给定的参数 n

输入的第二行有 K 互不相同的整数,第 i 个整数代表 Pi

输出格式

输出一行一个整数,代表答案

样例输入

4 19
2 3 5 7

样例输出

27

思考:

        

观察题目,我们不难得到以下结论:

  1. 每一个丑数,都是从已经有的丑数中迭代而来(初始给的素数集视为特殊情况)
  2. 每个丑数迭代的k个丑数中,我们仅需要优先考虑未出现的其中的最小值

倘若将每一个丑数分别乘以Pi后再比较,时间复杂度与空间复杂度无疑是巨大的,但是由结论2我们可以找到一个未出现的丑数Ui*Pi,其中Ui,Pi均只考虑一次,且应尽可能小,为了找到最小值,我们不妨以Pi作为基准,找到已有丑数中未与Pi相乘过的最小值。

如:给定的素数集2,3,5

已经计算得到的丑数为

2,3,4,5,6,8,9,10,12

对于2:2*2=4,2*3=6,2*5=10均已出现,不予考虑

对于3:3*2=6、3*3=9已出现,3*5=15,考虑15

对于4:4*2=8、4*3=12已出现,4*5=20,虽然20还未出现,但是计算对应的P3=5已经与更小的3相乘,故不予考虑

对于6:6*2=12已出现,6*3=18,考虑18

对于8:8*2=16,考虑16

由此我们得到了比较集:15,16,18,其中的最小值15即为下一个丑数

代码实现:

        不妨设U[0]=1作为基准丑数,U[ i ]表示第i个丑数,我们以素数集合为2,3,5,7为例,设Pt1,Pt2,Pt3,Pt4,初始值均为0,考虑U[ Pt1 ]*2,U[ Pt2 ]*3,U[ Pt3 ]*5,U[ Pt4 ]*7中的最小值,找到最小值U[ Pti ]*Pi后,其对应Pti+1,表示U[ Pti ]已经与Pi相乘过了,下一次比较时不再考虑,而下一个未与Pi相乘过的最小丑数为U[ Pti + 1 ],如果有多个值相同的情况,则相应Pti均+1,表示得到最小值相同的情况,如2*3与3*2所得丑数是一样的

拓展到给定素数集合有k个元素的情况,我们有:

        首先计算比较集合:c[ k ]

void getMatrix()
	{
		for (int i = 0; i < numOfPrime; i++)
		{
			c[i] = u[Pt[i]] * P[i];
		}
	}

        进行比较,返回最小值(即下一个丑数)同时对应标记Pti进位

	long long getMin()
	{
		long long tmp = c[0];
		for (int i = 1; i < numOfPrime; i++)
		{
			tmp = tmp < c[i] ? tmp : c[i];
		}
		for (int i = 0; i < numOfPrime; i++)//numOfPrime为素数集合中元素的个数
		{
			if (tmp == c[i]) Pt[i]++;
		}
		return tmp;
	}

        求第n个丑数:

long long getUgly()
	{
		for (int i = 1; i < target; i++)
		{
			getMatrix();
			u[i] = getMin();
		}
		return u[target - 1];
	}

        完整代码如下:

#include<iostream>
using namespace std;
class ugly
{
public:
	int numOfPrime;
	int target;
	long long* u;
	long long* P;
	long long* c;
	long long* Pt;
	ugly(int x,int y)
	{
		numOfPrime = x;
		target = y + 1;
		c = new long long[numOfPrime];
		u = new long long[target] { 1 };
		Pt = new long long[numOfPrime] {0};
		P = new long long[numOfPrime];
		for (int i = 0; i < numOfPrime; i++)
		{
			cin >> P[i];
		}
	}
	long long getMin()
	{
		long long tmp = c[0];
		for (int i = 1; i < numOfPrime; i++)
		{
			tmp = tmp < c[i] ? tmp : c[i];
		}
		for (int i = 0; i < numOfPrime; i++)
		{
			if (tmp == c[i]) Pt[i]++;
		}
		return tmp;
	}
	void getMatrix()
	{
		for (int i = 0; i < numOfPrime; i++)
		{
			c[i] = u[Pt[i]] * P[i];
		}
	}
	long long getUgly()
	{
		for (int i = 1; i < target; i++)
		{
			getMatrix();
			u[i] = getMin();
			//cout << u[i] << " ";(得到完整的丑数列,观察一下是否正确,不在题目要求内)
		}
		return u[target - 1];
	}
};
int main()
{
	int k, n;
	cin >> k >> n;
	ugly UglyNumber(k, n);
	cout<<UglyNumber.getUgly();
	return 0;
}

输出结果为(包含完整丑数序列):

      

  • 32
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值