1. 程式人生 > >PTA 7-12 整數分解為若干項之和(20 分)

PTA 7-12 整數分解為若干項之和(20 分)

將一個正整數N分解成幾個正整數相加,可以有多種分解方法,例如7=6+1,7=5+2,7=5+1+1,…。程式設計求出正整數N的所有整數分解式子。

輸入格式:

每個輸入包含一個測試用例,即正整數N (0 < N ≤ 30)。

輸出格式:

按遞增順序輸出N的所有整數分解式子。遞增順序是指:對於兩個分解序列 N1=n1,n2,

N2=m1,m2,,若存在 i 使得 n1=m1, , ni=mi,但是 ni+1<mi+1,則 N1 序列必定在 N2

序列之前輸出。每個式子由小到大相加,式子間用分號隔開,且每輸出 4 個式子後換行。

輸入樣例:

7

輸出樣例:

7
=1+1+1+1+1+1+1;7=1+1+1+1+1+2;7=1+1+1+1+3;7=1+1+1+2+2 7=1+1+1+4;7=1+1+2+3;7=1+1+5;7=1+2+2+2 7=1+2+4;7=1+3+3;7=1+6;7=2+2+3 7=2+5;7=3+4;7=7

解題思路:

採用了深度優先處理的思想,涉及到了一點點資料結構的知識。如果還沒學到資料結構,也不必擔心。在之前的題目中也可能用到了其它容易實現的資料結構,只是不知道它是資料結構中的內容。資料結構就是把各種各樣的操作、邏輯關係進行分類、總結,從而讓我們更加方便地設計演算法來解決問題。

深度優先演算法用遞迴寫起來比較方便。遞迴有兩個重要元素:

  • 遞迴出口
  • 遞迴的表示式

遞迴對技巧性要求很高,大多數時候其關係式並不是很容易找到。而且對遞迴的設計與理解,很容易鑽到具體細節的實現上。遞迴的優點就是可以讓一些複雜問題簡單化,把具體的細節交給計算機執行。而過分鑽研細節,就非常容易陷進去理不清頭緒。對於遞迴的學習應該是多看看經典的遞迴寫法,遇到類似問題會模仿寫就行了,不一定要自己創造出一個遞迴關係式。

本題也是如此。注意演算法的主體部分,關鍵資訊無非是:

void division () {

    division (下一個);
    對結點進行處理;
} 

遞迴出口是累加的總和等於了輸入的 N。

到這裡,就可以去看下面的程式碼了。然後試著自己寫,不會寫,就模仿,下面的框圖對寫這個演算法基本上沒有幫助——除了讓人覺得「好像挺複雜的」以外。遞迴的特點就是形式簡單,實際上細節繁多。不要扣於細節,先會寫了,再去思考和模擬它的執行細節以掌握它,這樣才不至於困難重重,無從下手。如果細節上有疑問,可以來看看下面的處理流程。

演算法的處理流程是:

  • 假設輸入的 N 為 3:
第一層遞迴 第二層遞迴 第三層遞迴 主要執行細節
division (1)
sum = 1,不跳出
division (1)
sum = 2,不跳出
division (1)
sum = 3 等於 N,輸出當前序列 1 1 1,
跳出,執行 for 迴圈,sum 均大於 3,跳出,返回上一層
第三層
s[0] s[1] s[2] 動作
1 1 1 輸出
1 1 2 跳出
1 1 3 跳出
1 1 4 跳出
開始處理
division (2)
sum = 3,輸出當前序列 1 2,然後跳出,執行 for 迴圈,均跳出
返回至上一層
返回至上一層 第二層
s[0] s[1] 動作
1 2 輸出
1 3 跳出
1 4 跳出
開始處理 division (2)
sum = 2,不跳出
division (2)
sum = 4,跳出,返回上一層
第二層
s[0] s[1] 動作
2 2 跳出
開始處理 division (3)
sum = 3, 輸出當前序列 3,結束程式
返回至上一層 第一層
s[0] 動作
3 跳出
  • 箭頭指明瞭各層之間的流動方向。

如果 N 更大一點,這個表格會變得更加複雜。遞迴的手動模擬範圍應儘量小一點,否則容易混亂。

你可以發現,所謂的深度優先就是說,優先處理下一個節點,直到它們的 sum 等於 N,才返回上一個節點。先爬到最深處,再往回走。

解題程式碼:

#include<stdio.h>

int N;

int s[31]; // 存放劃分結果 
int top = -1; // 陣列指標 
int count = 0; // 統計輸出的次數 
int sum = 0; // 拆分項累加和 

void division (int i);

int main ()
{
    scanf ("%d", &N);
    
    division (1);
    
    return 0; 
}

void division (int i) {
    if (sum == N) {
        count ++;
        printf("%d=", N);
        int k;
        for (k=0; k<top; k++) {
            printf("%d+", s[k]);
        }
        if (count%4 == 0 || s[top] == N) {
            printf("%d\n", s[top]);
        } else {
            printf("%d;", s[top]);
        }
        return;
    } // 輸出部分 
    if (sum > N) {
        return;
    }
    for (int j=i; j<=N; j++) {
        s[++top] = j;
        sum += j; 
        division (j);
        sum -= j;
        top --;
    } // 演算法主體 
}