1. 程式人生 > >#遞迴、DP# 2749: 分解因數

#遞迴、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;
}