丑陋的数字UVA
Ugly Numbers UVA
我正在尝试从 UVA 问题集 1500 号计算第 136 个丑陋的数字。
(参考: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=3&page=show_problem&problem=72)
我的算法很简单:
- 跟踪数组un中的所有丑陋数字。
- 设 un[i] 是 i+1 丑陋的数字。
步骤:
-
使用 tmp 变量 cur 来保存第 i 个丑陋数字的索引。
-
计算 un[cur] x 2、un[cur] x 3 和 un[cur] x5。
-
使用集合消除重复项并将它们存储到 un 中
-
对数组进行排序以确保 un[i+1] 始终尽可能小。
-
递增 cur 变量,使其成为第 i+1 个丑陋数字的索引。
-
重复直到数组中生成了 1500 个丑陋的数字。
我的代码:
# include<iostream>
# include<set>
# include<algorithm>
using namespace std;
int main(void) {
long long un[1500] = { 0 };
set<long long> us;
un[0] = 1;
us.insert(1);
int i = 1,unE = 0,cur = 0;
while(true) {
unE = 0;
sort(un,un+i);
if(us.find(un[cur]*2) == us.end()) {
un[i+unE] = un[cur]*2;
us.insert(un[i+unE]);
unE++;
}
if(i + unE > 1500 - 1) {
break;
}
if(us.find(un[cur]*3) == us.end()) {
un[i+unE] = un[cur]*3;
us.insert(un[i+unE]);
unE++;
}
if(i + unE > 1500 - 1) {
break;
}
if(us.find(un[cur]*5) == us.end()) {
un[i+unE] = un[cur]*5;
us.insert(un[i+unE]);
unE++;
}
i+=unE;
cur++;
}
sort(un,un+1500);
for(int i = 0; i < 1500; i++) {
cout << un[i] << " ";
}
cout << un[1500-1] << endl;
}
我的算法没有输出正确的数字,即859963392。相反,我得到了一个更大的数字。有人可以指出我正确的方向吗?
您的算法几乎是正确的,但是错误在于您不应该在生成 1500 个数字时停止,而是在 'curr' 达到第 1500 个数字时停止。之所以如此,是因为并非所有在"curr"之后的丑陋数字都已生成,您只能确定在任何时候"curr"之前都有所有丑陋的数字。优化算法的另一个建议是对"curr"之后的所有数字使用堆,这样你就不需要每次都对整个数组进行排序,也根本不需要使用集合。这是我的代码:
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
vector<long long> un; //using vector because we don't know how many ugly numbers we will need to generate
//If we decide to use an array (the way you did) it is better to define it outside of main().
priority_queue<long long> nun; //priority queue used for storing the next ugly numbers
const int TARGET=1500; //always good to store magical numbers as constants instead of having them appear all over the code
int main()
{
un.push_back(1); //adding the first ugly number
for (int i=0;i<TARGET-1;++i)
/*
We have already found the first ugly number (1),
so we only need to find TARGET-1 more.
*/
{
nun.push(-un[i]*2);
nun.push(-un[i]*3);
nun.push(-un[i]*5);
//adding the next ugly numbers to the heap
/*
Adding them as negative numbers because priority_queue
keeps the largest number on the top and we need the smallest.
*/
while (-nun.top()==un[i])
{
nun.pop();
//removing duplicates
/*
We can prove that we will never have more than 3 copies
of a number in the heap and thus that this will not
affect the performance.
1) We will never have more than one copy of a number in un.
2) Each number can be added to nun in 3 different ways:
by multiplying a number form un by 2, 3 or 5.
*/
}
un.push_back(-nun.top());
nun.pop();
//adding the next ugly number to un
}
cout<<un[TARGET-1]<<endl;
/*
Indexing starts at 0 so the TARGETth number is at index TARGET-1.
*/
return 0;
}
我的程序确实输出859963392,正确答案。
经过一番思考,我把它归结为线性复杂性。这是代码:
#include<iostream>
#include<vector>
//#include<conio.h>
using namespace std;
vector<long long> un; //using vector because we don't know how many ugly numbers we will need to generate
//If we decide to use an array (the way you did) it is better to define it outside of main().
const int TARGET=1500; //always good to store magical numbers as constants instead of having them appear all over the code
int l2=0,l3=0,l5=0; //store the indexes of the last numbers multiplied by 2, 3 and 5 respectively
int main()
{
un.push_back(1); //adding the first ugly number
for (int i=0;i<TARGET-1;++i)
/*
We have already found the first ugly number (1),
so we only need to find TARGET-1 more.
*/
{
un.push_back(min(min(un[l2]*2,un[l3]*3),un[l5]*5));
//adding the next ugly number to un
if (un[i+1]==un[l2]*2) //checks if 2 multiplied by the number at index l2 has been included in un, if so, increment l2
{
++l2;
}
if (un[i+1]==un[l3]*3) //checks if 3 multiplied by the number at index l3 has been included in un, if so, increment l3
{
++l3;
}
if (un[i+1]==un[l5]*5) //checks if 5 multiplied by the number at index l5 has been included in un, if so, increment l5
{
++l5;
}
/*
Basically only one of the variables l2, l3 and l5 (the one we used) will be incremented in a cycle unless we can get a number
in more than one way, in which case incrementing more than one of them is how we avoid duplicates.
Uncomment the commented code to observe this.
P.S. @PaulMcKenzie I can deal without a debugger just fine.
*/
//cerr<<i<<": "<<l2<<"("<<un[l2]*2<<") "<<l3<<"("<<un[l3]*3<<") "<<l5<<"("<<un[l5]*5<<") "<<un[i+1]<<endl;
//getch();
}
cout<<un[TARGET-1]<<endl;
/*
Indexing starts at 0 so the TARGETth number is at index TARGET-1.
*/
return 0;
}
第一个解决方案甚至根本不需要向量,因为它不使用前面的数字。因此,您可以使用单个变量在内存上对其进行优化。下面是这样的实现:
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
long long un; //last ugly number found
priority_queue<long long> nun; //priority queue used for storing the next ugly numbers
const int TARGET=1500; //always good to store magical numbers as constants instead of having them appear all over the code
int main()
{
un=1; //adding the first ugly number
for (int i=0;i<TARGET-1;++i)
/*
We have already found the first ugly number (1),
so we only need to find TARGET-1 more.
*/
{
nun.push(-un*2);
nun.push(-un*3);
nun.push(-un*5);
//adding the next ugly numbers to the heap
/*
Adding them as negative numbers because priority_queue
keeps the largest number on the top and we need the smallest.
*/
while (-nun.top()==un)
{
nun.pop();
//removing duplicates
/*
We can prove that we will never have more than 3 copies
of a number in the heap and thus that this will not
affect the performance.
1) We will never have more than one copy of a number in un.
2) Each number can be added to nun in 3 different ways:
by multiplying a number form un by 2, 3 or 5.
*/
}
un=-nun.top();
nun.pop();
//adding the next ugly number to un
}
cout<<un<<endl;
/*
Indexing starts at 0 so the TARGETth number is at index TARGET-1.
*/
return 0;
}
我们还可以通过释放最小l2
、l3
和l5
后面的内存来优化线性解决方案以使用更少的内存。请注意,第三个解决方案和第二个解决方案的优化版本都使用次线性内存,因为TARGET
进入无穷大,因为在极限下,几乎所有丑陋的数字都可以被 2、3 和 5 整除。因此,在每次迭代中,我们将每个指针移动一个(因此un
的长度不会改变),或者在堆解决方案的情况下,我们将 3 个数字添加到堆中,然后从堆中弹出 3 个数字(因此堆的大小不会改变)。事实上,通过更仔细的分析,我们可以看到内存像 TARGET^(2/3) 一样增长。
一个更简单编码但更难阅读的解决方案是:
#include <iostream>
using namespace std;
int main(){
const int n = 1499;
int a [ 1500 ];
int p1(0), p2(0), p3(0), end(0);
a [ 0 ] = 1;
while ( end < n ){
while ( a [ p1 ] * 2 <= a [ end ] ) ++ p1;
while ( a [ p2 ] * 3 <= a [ end ] ) ++ p2;
while ( a [ p3 ] * 5 <= a [ end ] ) ++ p3;
if ( a [ p1 ] * 2 < a [ p2 ] * 3 && a [ p1 ] * 2 < a [ p3 ] * 5 )
a [ ++ end ] = a [ p1 ++ ] * 2;
else if ( a [ p2 ] * 3 < a [ p3 ] * 5 )
a [ ++ end ] = a [ p2 ++ ] * 3;
else a [ ++ end ] = a [ p3 ++ ] * 5;
}
cout << "The 1500'th ugly number is " << a [ end ] << ".n";
return 0;
}
作为记录
,蛮力解决方案,只需检查所有数字是否丑陋并保持丑陋的数字在我的计算机上需要 20 多秒,使用以下代码:
//uva136 preparer
#include <iostream>
using namespace std;
typedef long long ll;
bool is_ugly(int in)
{
while( true)
{
if(in % 5 ==0)
in /= 5;
else if( in %2==0)
in /=2 ;
else if (in % 3 == 0 )
in/=3;
else
break;
}
if(in==1)
return true ;
else
return false ;
}
int main()
{
int c=0 ;
ll j=6;
int i=6;
for(j =6;(i<1501) ; j++)
{
if(isugly(j))
i++;
}
cout<<j-1<<endl<<double(clock())/CLOCKS_PER_SEC;// because at the last iteration of four , j is updated to j+1 and we should minus it by one to make it no count .
return 0;
}
- 比较并显示使用最小值(a,b)和最大值(a、b)升序排列的4个数字
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- 检查输入是否不是整数或数字
- 如何(从固定列表中)选择一个数字序列,该序列将与目标数字相加
- 如何用数字处理log(0)
- 最高有效数字侧的第N位
- 如何获取一个数字的前3位
- 查找最接近的大于当前数字的数字的索引
- 找到两对数字,使它们的乘积的绝对差最小化
- 我想做一个彼此不同但重复出现的数字
- 将数字转换为字母(例如:123 转换为一二三)
- C++如何计算用户输入的数字中的偶数位数
- 如何在C++中确定文本文件中的元素是字符还是数字
- 打印数字图案
- C++问题:用户认为数字1-100,程序提出问题不超过6次即可得到答案。无法正确
- 如何检查一个c++字符串中有多少相同的字符/数字
- 求出有多少个数字是完美平方,而sqrt()是L,R范围内的素数
- 将数字打印成文字
- 当使用比格式支持的精度更高的精度来显示数字时,会写出什么数据
- 丑陋的数字UVA