求正整数中第n个guly数,guly数是因式分解后,所有的因子最终必须是2、3或5的倍数。仅通过数学方法计算判断一个数是否是guly数,比较简单,但是为什么第n个guly数的求解是采用了dynamic programming思想呢?
Dynamic Programming下文简称DP,其思想的核心和问题的分辨是:后面的解答是前面解答的累加或累乘。
既然如此:我们再来看一看问题,guly序列当中,任意一个guly数必须是2||3||5的倍数,只不过是2,3,5其中一个或几个的第几倍而已,那么我们可以想一下,后面的一个数一定是前面某个guly的(2,3,5)倍数,为什么呢?因为所有的数都是2||3||5的倍数,但是这样思考好像又不对,因为2*7=14不是guly。
但是当后面的数是前面某个guly的倍数,而前面某个guly也是再前面某个guly的倍数,类推:再前面某个guly数一定是2、3或者5的倍数,则后面的数也必然是2,3,5的倍数(因为把所有前面guly的倍数一除,则剩下了再前面某个guly)。
至于第i个guly数是前面哪个guly的倍数,依据的原理是2,3,5当前顺序序号倍数的最小值,即我们依次让3个下标分别指向3个guly,但这3个下标a,b,c必须毫不动摇的分别指向2,3,5的倍数的guly数,我们再计算下一个guly时,将a,b,c所指的当前guly分别乘以2,3,5,得到3个guly数,则这3个数中的最小一个即是第i个guly数,然后将最小一个所对应的a,b,c下标增1,以指向2的下一个guly数(如果a==2),(注:这里讲解不够清晰,需深入理解guly数的依次排列规则)这样就保证了后面得到的guly就一定是前面guly的倍数即2,3,5的倍数,而且依次一个不漏地求出了顺序增大的guly数。
其DP思想是:后面的guly数依据前面已计算出的guly得出,不需要重复计算,利用了已计算得到的结果,并对前面guly数进行累乘来得到,所以提高了计算效率,这就是DP的本质。下面是实现的代码:
int DynamicProgramming::gulyDynamic(int n)
{
std::vector<int>guly;
guly.push_back(1); //我们初始化第一个guly数为1
//该方法的原理是:后面的数是前面的数的2||3||5倍
//所以可以利用dynamic方法:后面的数建立在前面的数的基础之上,即倍数或和
int result2, result3, result5;
std::vector<int>result;
int index2, index3,index5;//后面的数要么是2的倍数、3的倍数或者5的倍数,而这3者中最小的一个则是后面逐渐增加的一个
index2 =index3 =index5 = 0;
while (n>1)
{
result2 = guly[index2] * 2;
result3 = guly[index3] * 3;
result5 = guly[index5] * 5; //方法类似于归并排序中的conquer
result = conquerMin(result2, result3, result5);
for (int i = 0; i < result.size();i++)
{
if (i==0)
{
guly.push_back(result[i]); //返回最小的结果
n--;
}
else
{
switch (result[i])
{
case 2: index2++; break;
case 3:index3++; break;
default:index5++; break;
}
}
}//for(i)
if (n <= 1)
{
break;
}
}//while
return guly[guly.size() - 1]; //返回最后一个数即第n个guly
}
由于2,3,5的倍数中取最小的一个,所以采用了归并算法如下:
std::vector<int> DynamicProgramming::conquerMin(int num2, int num3, int num5)
{
std::vector<int>result;
std::vector<Point>num;
Point p2 = *new Point();
p2.setData(num2);
p2.setX(2);
num.push_back(p2);
Point p3 = *new Point();
p3.setData(num3);
p3.setX(3);
num.push_back(p3);
Point p5 = *new Point();
p5.setData(num5);
p5.setX(5);
num.push_back(p5);
//---------------------归并排序调用函数------------------------
mergeSort(num, 0, num.size() - 1);
int minNum = num[0].getData();
result.push_back(minNum);
for (int i = 0; i < num.size();i++)
{
if (num[i].getData()==minNum)
{
int index = num[i].getX();
result.push_back(index);
}
}
return result; //一个返回值包含2个不同意义的返回值
}
void DynamicProgramming::mergeSort(std::vector<Point>&num, int low, int high)
{
if (low<high)
{
int mid = (low + high) / 2;
mergeSort(num, low, mid);
mergeSort(num, mid + 1, high);
conquer(num, low, mid, high);
}
}
void DynamicProgramming::conquer(std::vector<Point>&num, int low, int mid, int high)
{
int i = low;
int j = mid + 1;
std::vector<Point>result;
while (i<=mid&&j<=high)
{
if (num[i].getData()<num[j].getData())
{
result.push_back(num[i]);
i++;
}
else
{
result.push_back(num[j]);
j++;
}
}
while (i<=mid)
{
result.push_back(num[i]);
i++;
}
while (j <= high)
{
result.push_back(num[j]);
j++;
}
for (int i = 0,k=low; i < result.size()&&k<=high;i++,k++)
{
num[k] = result[i];
}
}