为了方便举一反三,从本篇博客起附带题目。
描述
给出一个正整数a,要求分解成若干个正整数的乘积,即a = a1 * a2 * a3 * … * an,并且1 < a1 <= a2 <= a3 <= … <= an,问这样的分解的种数有多少。注意到a = a也是一种分解。
输入
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数a (1 < a < 32768)
输出
n行,每行输出对应一个输入。输出应是一个正整数,指明满足要求的分解的种数
/********************************************
**文件名:百炼-2749
**Copyright (c) 2015-2025 OrdinaryCrazy
**创建人:OrdinaryCrazy
**日期:20170807
**描述:百炼2749参考答案
**版本:1.0
*********************************************/
#include <stdio.h>
/*********************************************
这个问题的子问题不是很好找,
这是动态规划的一种题型,需要消除后效性,也就是原有的维度不够,
需要根据问题进行一次分解,增加维度,然后才能得到子问题
这个问题如果直接递归到因子的分法相乘,因为两个因子可能有公因子
所以会造成重解,要避免这种情况,我们可以这样想:
其实题目中有一个蛮关键的条件,就是说分解式要从小到大排列,这是一个
避免重解的突破口,我们就按照顺序来,首先对于第一个数,如果a(1)*a(1)>a
那么就与a(2)>a(1)矛盾,所以如果要枚举a1的可能值应该是2到a(1)*a(1)<=a
然后我们考虑对a/a(1)的分解问题,注意我们现在是有序的,也就是说a/a(1)的任何一个因子
都不能大于a(1),否则应该是另一种a1对应的情况,所以现在出的a(2)是相对于a(1)唯一解的,由此避免了
重解漏解问题,至于边界条件,如果我们现在要分解一个数a(m),结果我们发现它和a(m-1)之间再也找不到可以
分出去的数了,那么沿a(1)-a(m)就解除了一条从小到大的解
其实有点像树结构
**********************************************/
/*********************************************
**函数名:slove
**输入:num-现在需要分解的数,i-它因子的最小可能形式
**功能:计算num*i被表示成num*i=i*...形式的种数,每找到一条方法就使sum+1
**作者:OrdinaryCrazy
**日期:20170808
**版本:1.0
**********************************************/
int sum;
void solve(int num,int i)
{
if(num == 1)//如果a(m)和a(m-1)之间再没有因子,那么传入的i就是num自己,num/num=1
sum ++;
for(;i <= num;i++)
if(!(num % i))
solve(num/i,i);
}
int main()
{
int n,num,i;
scanf("%d",&n);
while(n--)
{
scanf("%d",&num);
int res = 1;
for(i = 2;i * i <= num;i++)
if(!(num % i))
{
sum = 0;
solve(num/i,i);
res += sum;
}
printf("%d\n",res);
}
return 0;
}