首先看題目要求:
傑克船長的煩惱
傑克船長這次運氣不錯,搶到了一大堆金幣。但他馬上又開始發愁了, 因爲如何給大家分金幣,一直都是件不容易的事,每次傑克船長都要頭疼好幾天。
關於分金幣,海盜的行規是這樣的:
-
每次行動,船長會根據各個海盜的表現記功,事後論功行賞,給大家分金幣。
-
分戰利品的時候,海盜們會隨意的站成一排,船長給每個人發一袋金幣。袋子裏的金幣數目有多有少,但船長保證每個海盜至少會分到一枚金幣。
-
拿到金幣後,相鄰的兩個海盜會互相比較。如果其中一個功勞高些,那他的金幣必須多一些,如果兩個人分數一樣,他們的金幣必須一樣。否則,海盜們就覺得不公平,要鬧事,甚至造反。
怎麼樣才能用最少的金幣讓大家都滿意呢? 領導這幫海盜可不輕鬆。
聽說這次比賽中有一位將來要成爲海賊王的男人,傑克船長相信他一定能解決這個麻煩。
輸入說明
在程序當前目錄下存在execute.stdin文件,程序從execute.stdin中取得輸入數據。
execute.stdin中存放着N(N<100)個十進制正整數,數字之間用空格格開。
每個數字都代表某個海盜的功勞,值範圍爲(1, 100)。
輸出說明
輸出一個正整數,代表讓所有海盜都滿意的最少的金幣數。
示例
execute.stdin內容: 1 3 3 2
預期輸出:6
說明:符合要求的金幣分法是 1 2 2 1
題目解讀:就是有一串數,給這串數種的每個數附一個權,權儘量小,但是兩個相鄰的數,本身大的數,其權必大於其相鄰的小的數。
思路,採用排序樹的思路,不過不是二叉樹,每個節點只有左子樹或者只有右子樹,左子樹<父節點,右子樹>=父節點,父節點的權值等於左子樹深度,或者等於前一個權加1。
圖中主要介紹了這種思想,注意右側樹葉,沒有左孩子也沒有右孩子,要麼等於父節點,要麼等於父節點+1。
最複雜的是中間的右分支,相等的情況都出現這裏。儘量向父節點靠,即如果有一個節點的值等於父節點,那麼他的權應該儘量等於父節點,但是如果這個節點是右側的轉折點,那麼父節點要想該節點靠,即權等於該節點。
算法如下:
#include<iostream>
#include<vector>
#include<fstream>
using namespace std;
//基於二叉排序樹
typedef struct SortTree
{
SortTree* lchild;
SortTree* rchild;
int val;
SortTree(int x)
{
val = x;
}
}*STree;
STree CreateSTree(vector<int>vec)
{
STree tree = new SortTree(vec[0]);
tree->lchild = NULL;
tree->rchild = NULL;
STree pTree = tree;
for (int i = 1; i < vec.size(); i++)
{
if (vec[i] < vec[i - 1])
{
pTree->lchild = new SortTree(vec[i]);
pTree->rchild = NULL;
pTree = pTree->lchild;
}
else
if (vec[i] >= vec[i - 1])//相等的情況放在右子樹,因爲我們用左子樹深度判定金幣
{
pTree->rchild = new SortTree(vec[i]);
pTree->lchild = NULL;
pTree = pTree->rchild;
}
}
pTree->lchild = NULL;
pTree->rchild = NULL;
return tree;
}
void TreePrint(STree tree)
{
if(tree)
{
cout << tree->val << " ";
TreePrint(tree->lchild);
TreePrint(tree->rchild);
}
}
//右子樹比左子樹大1,左子樹深度+1是根的金幣
//我們的數據要麼有左子樹要麼有右子樹,不可能同時有左右子樹
int deep(STree tree)
{
int num = 0;
STree deepTree = tree;
while (deepTree->lchild!=NULL)
{
num++;
deepTree = deepTree->lchild;
}
num++;
return num;//左子樹深度
}
bool parent_has_lchild(STree tree ,int i)//搜索i節點的父節點是否有左子樹
{
for (int j = 0; j < i-1; j++)
{
if (tree->lchild)
{
tree = tree->lchild;
}
else
if (tree->rchild)
{
tree = tree->rchild;
}
}
if (tree->lchild)
{
return true;
}
else
return false;
}
bool father_equal_child(STree tree, int i)
{
for (int j = 0; j < i - 1; j++)
{
if (tree->lchild)
tree = tree->lchild;
else
if (tree->rchild)
tree = tree->rchild;
}
if (tree->lchild)
{
if (tree->val == tree->lchild->val)
return true;
}
else
if (tree->rchild)
{
if (tree->val == tree->rchild->val)
{
return true;
}
}
return false;
}
vector<int>deepjihe(STree tree,int length)
{
vector<int>vec;
STree m_deep = tree;
for (int i = 0; i < length; i++)
{
if (m_deep->lchild && m_deep==tree)//頭節點有左孩子
{
//有左子樹
int ldeep = deep(m_deep);
vec.push_back(ldeep);
m_deep = m_deep->lchild;
//左子樹上的值,以此賦值,不能放在後面處理
}
else
if (m_deep->lchild && m_deep != tree && !parent_has_lchild(tree,i))//不是頭節點,有左孩子,要判斷左孩子深度與父節點的大小,如果大,就=深度,否則,=父節點+1
{
int ldeep = deep(m_deep);//取深度
if (ldeep >= vec[i - 1])
{
vec.push_back(ldeep);
m_deep = m_deep->lchild;
}
else
{
vec.push_back(vec[i - 1] + 1);
m_deep = m_deep->lchild;
}
}
else
if (m_deep->lchild && m_deep != tree && parent_has_lchild(tree, i))
{
int ldeep = deep(m_deep);//取深度
vec.push_back(ldeep);
m_deep = m_deep->lchild;
}
else
if(m_deep->rchild)
{
if (m_deep == tree)//頭節點
{
vec.push_back(1);
m_deep = m_deep->rchild;
}
else
{
if (parent_has_lchild(tree, i))//判斷i節點的父節點是否有左孩子,有的話,i節點對應的是1,不然,前一個值+1
{
vec.push_back(1);
m_deep = m_deep->rchild;
}
else
if(!parent_has_lchild(tree, i))
{
vec.push_back(vec[i - 1] + 1);
m_deep = m_deep->rchild;
}
}
}
else
if (m_deep->rchild == NULL && m_deep->lchild == NULL && parent_has_lchild(tree,i))//父節點有左孩子
{
vec.push_back(1);
}
else
if (m_deep->rchild == NULL && m_deep->lchild == NULL && !parent_has_lchild(tree, i))
{
if (father_equal_child(tree,i))//判斷父節點的val與該節點的大小
vec.push_back(vec[i - 1]);
else
vec.push_back(vec[i - 1] + 1);
}
}
return vec;
}
//判斷當前節點是否有左孩子
bool cur_has_lchild(STree tree, int i)
{
for (int j = 0; j < i; j++)
{
if (tree->lchild)
{
tree = tree->lchild;
}
else
if (tree->rchild)
{
tree = tree->rchild;
}
}
if (tree->lchild)
{
return true;
}
else
if (tree->rchild)
{
return false;
}
return false;
}
bool is_right_turn(STree tree, int i)
{
if (!parent_has_lchild(tree, i) && cur_has_lchild(tree, i))
{
return true;
}
else
return false;
}
bool is_left_turn(STree tree, int i)
{
if (parent_has_lchild(tree, i) && !cur_has_lchild(tree, i))
{
return true;
}
else
return false;
}
//這個函數的作用是修正數組,從根節點依次判斷,如果是轉折點(父節點有右孩子,自己有左孩子),
//那麼都得向這個方向靠攏,我們的樹把相等的放在了右孩子上了
vector<int>hasEqual(STree tree, vector<int>vec,vector<int>vec1)
{
vector<int>vec2;
vec2 = vec1;
for (int i = 1; i < vec.size(); i++)
{
if (father_equal_child(tree,i))//父節點與子節點相等
{
if (is_right_turn(tree, i) && !is_left_turn(tree, i - 1))//當前節點是右轉折點,前一個節點不是左轉折點
{
vec2[i - 1] = vec2[i];
}
else
if (!is_right_turn(tree, i) && is_left_turn(tree, i - 1))//當前節點不是右轉折點,前一個節點是左轉折點
{
vec2[i] = vec2[i - 1];
}
else
{
vec2[i] = vec2[i - 1];
}
}
}
return vec2;
}
int sum(vector<int>vec)
{
int sum = 0;
for (int i = 0; i < vec.size(); i++)
{
sum = sum + vec[i];
}
return sum;
}
int main()
{
ifstream ifs;
ifs.open("execute.stdin");
int num = 0;
vector<int>vec;
while (ifs >> num)
{
vec.push_back(num);
}
ifs.close();
STree tree = CreateSTree(vec);
vector<int>vec1;
vec1 = deepjihe(tree, vec.size());
vector<int>vec2;
vec2 = hasEqual(tree,vec,vec1);
int sump = 0;
sump = sum(vec2);
cout << sump << endl;
return 0;
}
PS:本人是算法小白,研究方向也不再此,只是想出來一個小想法並且進行了實現,有不好之處請指教。