整數分解(划分)
分解和
· 給定一個整數n,找到k個數,使得其和等於n。
樣例:
4 = 1+1+1+1
4 = 1+1+2
4 = 1+3
4 = 2+2
4 = 4
求其分解的所有可能,並輸出分解表達式。
思路:要拆分整數n,肯定先要找到一個元素,然后我們會發現,剩下的問題還是一個整數分解問題,因此容易得到問題的解。
定義函數 f(n) 為 n 可以拆分的解的個數,即可以先拆分出一個數字k ( k = 1,2,……,n),然后再拆分 f(k) ,可以得出有:
n = 1+f(n-1);
n = 2+f(n-2);
.....
n = (n-1)+f(1);
n = n+f(0);`
再來想一想該用怎樣的數據結構來存儲,很明顯用數組,int res[max] 用來暫存拆分元素。

先拿簡單的拆分來說事,拆分成兩個數的情況。
//只拆分為兩個數的時候
void resolve(int n)
{
//res用來暫存拆分元素
//p是游標,初始值為p=0;
for(int i=1;i<=n;i++)
{
res[p]=i;
p++;//下一個位置存儲n-i;
res[p]=n-i;
//輸出處理
p--;//回歸到起點。
}
}
分析上面的代碼,再來看遞推,如果分成三個數,那么在即將執行 res[p] = n - i 時,需要將 n - i 在一次分解成兩個數,即 resolve( n - i ),這樣就形成了遞歸。
那么遞歸的出口條件是什么呢,當需要分解的數是0時,已經不能在繼續往下分了,當進入遞歸出口的時候,表明本次已經分解完成,可以輸出res中的數據。
void resolve(int n)
{
if(n<=0)
{
for (int i=0; i<p_res; i++) //輸出
cout << res[i] << " ";
cout << endl;
return ;
}
for(int i=1;i<=n;i++)
{
res[p]=i;
p++;
resolve(n-i);
p--;
}
}
但這還有個問題,如當n=4時,會輸出1 3,和3 1兩組解,其實他們是相同的。
如果要求答案不能重復呢?
同樣,我們可以定義一個函數f(n, min_factor),其中min_factor表示n拆分后元素中的最小值,這樣即可通過min_factor來限制for循環的初始值,達到拆分元素從小到大輸出的目的,從而避免相同的解重復輸出
貼出完整可運行代碼如下:
#include <iostream>
using namespace std;
#define MAX 20
int res_num;
// 拆分元素暫存在res數組中
int res[MAX];
int p = 0;
// 將n進行拆分
void resolve(int n, int min_factor=1);
int main() {
while (1) {
int n;
cin >> n;
resolve(n,1);
cout << "total num of res:\t" << res_num << endl;
res_num = 0;
}
return 0;
}
void resolve(int n, int min_factor) {
if (n<=0) { // 出口
for (int i=0; i<p; i++)
cout << res[i] << " ";
cout << endl;
res_num++;
}
for (int i=min_factor; i<=n; i++) { // 此處修改
res[p] = i;
p++; // p++ 來順序存儲各個拆分元素
resolve(n-i, i);// 此處修改
p--; // 此行必須有,執行完這一行,下一次for循環才能回退
}
}
分解積
· 將一個數n的分解為因子的乘積形式,輸出所有可能,並輸出表達式。
12 = 2*2*3;
12 = 2*6;
12 = 3*4;
12 = 6*2;
12 = 12*1;
解法與上述大同小異,不再贅述。
#include<iostream>
using namespace std;
int data[100];
int p=0;
int num=0;
int x;
void resolve(int n,int min)
{
if(n<2)
{
num++;
cout<<x<<"=";
for(int j=0;j<p;j++)
{
cout<<data[j];
if(j!=p-1)
cout<<"*";
if(data[j]==x)
cout<<"*1";
}
cout<<endl;
return ;
}
for(int i=min;i<=n;i++)
{
if(n%i==0)
{
data[p]=i;
p++;
resolve(n/i,i);
p--;
}
}
}
int main()
{
while(cin>>x)
{
resolve(x,2);
cout<<"The totla num is "<<num<<endl;
cout<<"--------------------------"<<endl;
}
}
動態規划之整數划分
整數划分: 指把一個正整數n寫成多個大於等於1且小於等於其本身的整數的和,則其中各加數所構成的集合為n的一個划分。這是一個典型的遞歸算法。
1、問題描述和分析
對於一個正整數n的分划,就是把n表示成一系列正整數之和的表達式。
注意,分划與順序無關,例如6=1+5 和 6=5+1被認為是同一個划分。另外,這個整數n本身也算是一種分化。
分析:
所謂整數划分,是指把一個正整數n寫成為
其中,mi
為正整數,並且 ;
為n的一個划分。
如果 中的最大值不超過m,即
,則稱它屬於n的一個m划分。
例如:當n=4時,他有5個划分:{4}, {3,1}, {2,2}, {2,1,1}, {1,1,1,1};
2、數據結構和算法
該問題是求出n的所有划分個數,即f(n, m)。下面我們考慮求f(n,m)的方法,采用遞歸法, 根據n和m的關系,考慮以下幾種情況:
(1) 當n=1時,不論m的值為多少(m>0),只有一種划分即{1};
(2) 當m=1時,不論n的值為多少,只有一種划分即n個1,{1,1,1,…,1};
(3) 當n=m時,根據划分中是否包含n,可以分為兩種情況:
(a) 划分中包含n的情況,只有一個即{n};
(b) 划分中不包含n的情況,這時划分中最大的數字也一定比n小,即n的所有(n-1)划分。
因此 f(n,n) =1 + f(n,n-1);
(4) 當n<m時,由於划分中不可能出現負數,因此就相當於f(n,n);
(5) 但n>m時,根據划分中是否包含最大值m,可以分為兩種情況:
(a) 划分中包含m的情況,即{m, {x1,x2,…xi}}, 其中{x1,x2,… xi} 的和為n-m,因此這情況下為f(n-m,m);
(b) 划分中不包含m的情況,則划分中所有值都比m小,即n的(m-1)划分(離散性),個數為f(n,m-1);
因此 f(n, m) = f(n-m, m)+f(n,m-1);
#include<stdio.h>
int Divintege(int n,int m)
{
if(n==1||m==1)
return 1;
else if(n<m)
return Divintege(n,n);
else if(n==m)
return 1+Divintege(n,n-1);
else
return Divintege(n,m-1)+Divintege(n-m,m);
}
int main(void)
{
int n;
while(scanf("%d",&n)!=EOF&&(n>=1))
{
printf("%d\n",Divintege(n,n));
}
return 0;
}
說明
轉載自(有刪改):整數分解方法_casion-CSDN博客_整數拆分公式
如有侵權請聯系博主刪除