#遞迴、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;那麼其可以由兩種互斥的情況組成:
- 如果最終的劃分結果有至少一個 j 那麼其種類數為 dp[ i/j ][j] (注意下次最大劃分數仍是 j,表示下一次仍然可以劃分出 j,如果要求分解的每個數不重複,那麼改為 dp[ i/j ][ j-1 ] 即可)
- 如果最終的劃分結果連一個 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;
}