1. 程式人生 > >HDU -- 數塔(ACM Step: 3.2.7)

HDU -- 數塔(ACM Step: 3.2.7)

一、概述

1. 問題描述

一個由數字構成的塔,第i層有i個節點,i=1,2,...,n,每個節點帶有一個權值,用v[i]表示,問從塔頂走到塔底最大能夠獲得多少權值?每一步只能走向相鄰的節點。一個5層的數塔如圖1.1所示。

圖1.1  5層的數塔

2. 問題連結

3. 問題截圖

圖1.12 問題截圖

二、演算法思路

一個n層的數塔,一共\frac{\left ( n+1 \right )\times n }{2}個節點,並且通過分析可以知道,問題的解需要從每一層中取得一個節點構成。

假設,構成問題的解的節點序列是,a_{1},a_{2},...,a_{n}a_{i}表示第i層的某節點,由於從塔頂到塔底的每一步只能走向相鄰的下一層的節點,因此,可以推斷出解序列的元素有如下關係:

a_{i}\in \left \{ a_{i-1}+i-1,a_{i-1}+i \right \},i=2,3,...,n

下面以一個四層的數塔分析問題,數塔中的數字代表節點的編號。

        1

      2  3

    4  5  6

  7  8  9  10

問題所求是節點1作為根節點時,對應數塔的最大權值,通過分析,這個最大值是,節點2和節點3對應數塔的最大值中的較大者,即是節點2、3作為根節點時對應數塔的最大值中的較大者;要求節點2對應數塔的值,同樣要分析它對應的下層節點中的最大值。通過分析,發現可以用遞迴的思想解決,同時,由於從上向下遞迴時,同一個子問題可能被多次求解,比如求解以節點2和節點3為根節點的最大值時,它們都要取得節點5對應數塔的最大值,這會導致一些不必要的工作量。

由於要求出所有節點作為根節點對應的最大權值,可以從下往上構造,這樣可以保證對每個節點只需要計算一次。

因此演算法從最後一層節點開始計算,他們對應的最大權值就是他們本身的權值,然後根據上述公式:a_{i}\in \left \{ a_{i-1}+i-1,a_{i-1}+i \right \},i=2,3,...,n,可以構造出上層的權值最大值。

三、演算法實現

#include <iostream>    // for cin, cout, endl

using std::cin;
using std::cout;
using std::endl;

const int MAXSIZE = ((1+100)*100)/2;    // max of the number of elements of digit tower
unsigned int data[MAXSIZE];    // store the input data
unsigned int ans[MAXSIZE];    // ans for answer, the data structure that construct the result, ans[0] is always the result

int input();    // for input data
unsigned calculate(int);    // for calculate result
void print(unsigned);    // for print result

int main()
{
    int T, n;
    unsigned m;

    cin >> T;

    for(int i=0; i<T; i++){
        n = input();

        m = calculate(n);

        print(m);
    }
    return 0;
}

// read input data, return the depth of digit tower
int input()
{
    int ret, n;

    cin >> ret;

    n = ((ret+1)*ret)/2;    // total input data

    for (int i=0; i<n; i++)
        cin >> data[i];

    return ret;
}

// calculate the result, n indicate the depth of digit tower, return the result of depth of n
unsigned calculate(int n)
{
    int ele_num = ((1+n)*n)/2;    // the actual number of elements in digit tower
    int i, j, k;

    // calculate the max value of the elements of last layer,
    // max value is itself because they do not have next layer element when they be treated as the first node of digit tower
    for (i=ele_num-1, j=n; j>0; i--, j--)
        ans[i] = data[i];

    // i indicate the node that it is currently calculated for max value
    // j indicate the layer of node i
    // k keep track of the num of node of layer j, when k reach 0, j incicate the prior layer
    for (j=n-1, k=j; i>=0; i--, k--){
        if(k == 0)
            k = --j;
        // the next layer node that related with i is node i+j and i+j+1
        if(ans[i+j] < ans[i+j+1])
            ans[i] = ans[i+j+1]+data[i];
        else
            ans[i] = ans[i+j]+data[i];
    }
    return ans[0];
}

// print the result
void print(unsigned n)
{
    cout << n << endl;
}