整數分解(划分)


整數分解(划分)

分解和

· 給定一個整數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寫成為

\[n=m_1+m_2+\cdots +m_i \]

其中,mi為正整數,並且imgimg 為n的一個划分。

如果img 中的最大值不超過m,即img ,則稱它屬於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);

image-20210818212659859

#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博客_整數拆分公式

如有侵權請聯系博主刪除


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM