#递归、DP# 2749: 分解因数

题目链接:http://bailian.openjudge.cn/practice/2749/

描述

给出一个正整数a,要求分解成若干个正整数的乘积,即a = a1 * a2 * a3 * ... * an,并且1 < a1 <= a2 <= a3 <= ... <= an,问这样的分解的种数有多少。注意到a = a也是一种分解。

输入

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数a (1 < a < 32768)

输出

n行,每行输出对应一个输入。输出应是一个正整数,指明满足要求的分解的种数

 

Solution:

定义 dp[i][j] 表示将 i 分解成不大于 j 的因数的个数
i 如果能分解出来 j ,即为 i%j == 0;那么其可以由两种互斥的情况组成:

  1. 如果最终的划分结果有至少一个 j 那么其种类数为 dp[ i/j ][j] (注意下次最大划分数仍是 j,表示下一次仍然可以划分出 j,如果要求分解的每个数不重复,那么改为 dp[ i/j ][ j-1 ] 即可)
  2. 如果最终的划分结果连一个 j 都没有,那么其种类数为 dp[i][ j-1 ]

比如  8%2==0,dp[8][2] = dp[4][2] + dp[8][1],前面的是至少有一个2(然后把这个2划分出去,剩下的部分组合的数量是 dp(4, 2)),后面的是没有划分出2(那么划分出来最大的数就是1),i 如果不能分解 j,即为 i%j != 0 ,那么 dp[i][j] = dp[i][ j-1 ]。

总结:
if( i%j ==0 )  dp[i][j] = dp[i][ j-1 ] + dp[ i/j ][j];
else  dp[i][j] = dp[i][ j-1 ];


然后寻找出口
1.  if( j>i ) dp[i][j] = dp[i][i]
2.  dp[1][1] = dp[1][0] + dp[1][1] 
3.  dp[2][2] = dp[2][1] + dp[1][1] = 1  
根据上面两行推导出 dp(2, 1) = 0,dp[1,1] = 1,可以推导if( i>1 )  dp(i,1) = 0。

总结得到出口条件:
i==1  return 1;
j==1  return 0;
注意上面两行是有顺序的,不能颠倒。

 

参考博客:https://www.cnblogs.com/MapReduce/p/8376190.html

 

Code:

解法一:

int solve(int i, int j)  //本题数据量太小 ,不用记录状态,递归即可直接A
{
    if(i == 1) return 1;
    if(j == 1) return 0;
    if(i%j == 0) return solve(i/j, j) + solve(i, j-1);
    return solve(i, j - 1);
}
int main()
{
    int n;
    cin >> n;
    while(n--){
        int x; cin >> x;
        int ans = solve(x, x);
        cout << ans << endl;
    }
    return 0;
}

解法二:

int dp(int i, int n) {
	int ans = 1;
	for(; i <= sqrt(n); i++) {
		if(n % i == 0) ans += dp(i, n/i);
	}
	return ans;
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--) {
        int a;
        scanf("%d", &a);
    	printf("%d\n", dp(2, a));
    }
    return 0;
}

 

你可能感兴趣的:(动态规划,递归,&,DFS)