[C++] 2017聯發科技杯編程挑戰賽 複賽題 「傑克船長的煩惱」


題目以下。ios

規則

傑克船長此次運氣不錯,搶到了一大堆金幣。但他立刻又開始發愁了, 由於如何給你們分金幣,一直都是件不容易的事,每次傑克船長都要頭疼好幾天。算法

關於分金幣,海盜的行規是這樣的:函數

  • 每次行動,船長會根據各個海盜的表現記功,過後論功行賞,給你們分金幣。
  • 分戰利品的時候,海盜們會隨意的站成一排,船長給每一個人發一袋金幣。袋子裏的金幣數目有多有少,但船長保證每一個海盜至少會分到一枚金幣。
  • 拿到金幣後,相鄰的兩個海盜會互相比較。若是其中一個功勞高些,那他的金幣必須多一些,若是兩我的分數同樣,他們的金幣必須同樣。不然,海盜們就以爲不公平,要鬧事,甚至造反。

怎麼樣才能用最少的金幣讓你們都滿意呢? 領導這幫海盜可不輕鬆。code

據說此次比賽中有一位未來要成爲海賊王的男人,傑克船長相信他必定能解決這個麻煩。blog

輸入說明

在程序當前目錄下存在execute.stdin文件,程序從execute.stdin中取得輸入數據。
execute.stdin中存放着N(N<100)個十進制正整數,數字之間用空格格開。
每一個數字都表明某個海盜的功勞,值範圍爲(1, 100)。ci

輸出說明

輸出一個正整數,表明讓全部海盜都滿意的最少的金幣數。get

算法思路

  • 找出序列中全部的局部最小值,給對應位置的船員分配一枚金幣,而後兩側的依次金幣數加1。io

  • 在兩個局部最小值之間有一個局部最大值,例如一段序列... 11 12 13 14 12 11 ... ,若兩側的11是局部最小值,14是局部最大值,則從左往右,11到13的位置分別分配1 2 3枚金幣;從右往左,11到12的位置分別分配1 2枚金幣,14的位置若是分配3枚金幣的話不能知足比13多的條件,因此14的位置分配4個金幣。因此,從兩側的局部最小值依次遞增到局部最大值位置的時候,局部最大值對應位置取較大的金幣數。stream

  • 對於序列的最左和最右位置,若是第一個數小於/大於第二個數,則其是局部最小/大值;若是最後一個數小於/大於倒數第二個數,則其是局部最小/大值;基礎

源代碼

#include <iostream>
#include <fstream>
#include <vector>

using std::vector;
// vector做爲函數參數和返回值表示法
void money(const vector <int> & value);
vector <int> findLocalMin(const vector <int> & vec);

int main()
{
    /*打開文件,獲取功勞值信息*/
    std::ifstream file;
    file.open("execute.stdin");

    int temp;
    vector <int> value;  // 存儲功勞值

    if (!file)
    {
        std::cout << "Error!";
    }
    while (file >> temp)
    {   
        value.push_back(temp);      
    }
    file.close();
    

    /*計算金幣數*/
    money(value);

    //std::cin.get();
    return 0;
}

void money(const vector <int> &value)
{

    // 去除重複的數
    int length = value.size();
    vector <int> newValue;

    newValue.push_back(value[0]);
    for (int i = 1; i < length; ++i)
    {
        if (value[i] != value[i - 1])
        {
            newValue.push_back(value[i]);  // 抽取不重複的數
        }
    }
    

    // 找局部最小值位置
    vector <int> minLocalPos = findLocalMin(newValue);


    // 分配金幣(對應不重複的序列)
    
    int len = newValue.size();
    vector <int> gold;  // 放置金幣(對應原始序列)
    vector <int> newGold(len, 1);  // 放置金幣(對應不重複的序列)
    

    if (minLocalPos[0] > 0)  // 剛開始是遞減
    {
        for (int k = minLocalPos[0] - 1; k >= 0; --k)
        {
            newGold[k] = newGold[k+1] + 1;  // 在後一個的基礎上加一
        }
    }
    
    for (int i = 1; i < minLocalPos.size(); ++i)
    {
        for (int j = minLocalPos[i - 1] + 1; j < minLocalPos[i]; ++j)  //從前日後
        {
            if (newValue[j] > newValue[j - 1])
            {
                newGold[j] = newGold[j-1] + 1;  // 在前一個的基礎上加一
            }
        }
        for (int j = minLocalPos[i] - 1; j > minLocalPos[i - 1]; --j)  //從後往前
        {
            // 若是此方向的梯度更大
            if ((newValue[j] > newValue[j + 1]) && (newGold[j + 1]+1 > newGold[j]))
            {
                newGold[j] = newGold[j + 1] + 1;  // 在後一個的基礎上加一
            }
        }
    }

    int end = minLocalPos[minLocalPos.size() - 1];
    if (len - 1 > end)  // 末尾是單調增的
    {
        for (int i = end + 1; i < len; ++i)
        {
            newGold[i] = newGold[i - 1] + 1;  // 在前一個的基礎上加一
        }
    }

    /*for (int k = 0; k < newGold.size(); ++k)
    {
        std::cout << newGold[k] << " ";
    }*/


    // 分配金幣(對應原始的value序列)
    int j = 0;
    int i = 0;
    while(i < length)
    {
        if (value[i] == newValue[j])
        {
            gold.push_back(newGold[j]);
            ++i;
        }
        else
        {
            ++j;
        }
    }

    // 計算金幣和
    int sum = 0;
    for (int k = 0; k < gold.size(); ++k)
    {
        //std::cout << gold[k] << " ";
        sum += gold[k];
    }
    std::cout << sum;
}


// 找局部最小值的位置
vector <int> findLocalMin(const vector <int> & vec)
{
    vector <int> minLocalPos;
    int len = vec.size();

    if (len == 1)
    {
        minLocalPos.push_back(0);
        return minLocalPos;
    }

    // len >= 2
    if (vec[0] < vec[1])
    {
        minLocalPos.push_back(0);
    }
    
    for (int i = 1; i < len - 1; ++i)
    {
        if ((vec[i] < vec[i - 1]) && (vec[i] < vec[i + 1]))
        {
            minLocalPos.push_back(i);
        }

    }

    if (vec[len - 1] < vec[len - 2])
    {
        minLocalPos.push_back(len - 1);
    }


    return minLocalPos;
}

運行結果

相關文章
相關標籤/搜索