題目描述
農夫要修理牧場的一段柵欄,他測量了柵欄,發現需要N塊木頭,每塊木頭長度爲整數Li個長度單位,於是他購買了一條很長的、能鋸成N塊的木頭,即該木頭的長度是Li的總和。
但是農夫自己沒有鋸子,請人鋸木的酬金跟這段木頭的長度成正比。爲簡單起見,不妨就設酬金等於所鋸木頭的長度。例如,要將長度爲20的木頭鋸成長度爲8、7和5的三段,第一次鋸木頭花費20,將木頭鋸成12和8;第二次鋸木頭花費12,將長度爲12的木頭鋸成7和5,總花費爲32。如果第一次將木頭鋸成15和5,則第二次鋸木頭花費15,總花費爲35(大於32)。
請編寫程序幫助農夫計算將木頭鋸成N塊的最少花費。
輸入格式:
輸入首先給出正整數N(≤10000),表示要將木頭鋸成N塊。第二行給出N個正整數(≤50),表示每段木塊的長度。
輸出格式:
輸出一個整數,即將木頭鋸成N塊的最少花費。
輸入樣例:
8
4 5 1 2 1 3 1 1
輸出樣例:
49
思路分析
第一種思路(運行超時)
先寫個排序算法,對輸入數組進行排序,然後選取最小的兩個數進行相加,然後把這個數在數組裏面刪除,把得到的結果加進去,在循環以上操作,直到數組裏面只有一個數退出。最終結果爲每一次得到的結果的累加值。
#include <stdio.h>
#include<stdbool.h>
void sort(int a[],int n){
int i, j, temp;
for(i=0; i<n-1; i++){
for(j=i+1; j<n; j++){
if(a[i] > a[j]){
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
int main() {
int a[10001];
int n,i,sum=0,temp=0;
scanf("%d",&n);
for (i = 0; i < n; i++) {
scanf("%d",&a[i]);
}
if(n==1)
printf("%d\n",0);
else if(n==2)
printf("%d\n",a[0]+a[1]);
else{
while(true){
sort(a,n);
if(a[n-2]==0)
break;
for (i = 0; i < n; i++) {
if(a[i]!=0){
temp=a[i]+a[i+1];
a[i]=0;a[i+1]=temp;
sum+=temp;
break;
}
}
}
printf("%d\n",sum);
}
return 0;
}
第二種思路(來源於百度)
可以用哈弗曼樹解決這個問題,先將輸入的數都申請爲內存節點(樹),找到輸入的最小的數和次小的數的下標,然後建立父節點,值爲他們的和。把最小的數對應的下標的樹變成NULL,次小的數對應的下標的樹變成父節點(父樹),然後循環上面的操作。最後求得所有非葉節點的和,可以由所有葉節點深度和權值的乘機之和求出。原文鏈接
這種算法比上面的算法時間複雜度要低,可以過PTA的檢測,因爲在尋找最小的兩個值時,第一種思路是排序而第二種思路是找到最小的下標,會少一層循環。
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct HuffmanTreeNode{
ElemType data; //哈夫曼樹中節點的權值
struct HuffmanTreeNode* left;
struct HuffmanTreeNode* right;
}HuffmanTreeNode,*PtrHuffman;
PtrHuffman createHuffmanTree(ElemType arr[],int max){
PtrHuffman ptrArr[max];
PtrHuffman ptr,pRoot=NULL;
for (int i = 0; i < max; i++){ //初始化結構體指針數組,數組中每一個元素爲一個結構體指針類型
ptr = (PtrHuffman)malloc(sizeof(HuffmanTreeNode));
ptr->data = arr[i];
ptr->left = ptr->right = NULL;
ptrArr[i] = ptr;
}
for(int i = 1; i < max; i++)
{ //進行 n-1 次循環建立哈夫曼樹
//k1表示森林中具有最小權值的樹根結點的下標,k2爲次最小的下標
int k1 = -1, k2;
for(int j = 0; j < max; j++){
if (ptrArr[j] != NULL && k1 == -1){
k1 = j;
continue;
}
if (ptrArr[j] != NULL){
k2 = j;
break;
}
}
//將指針數組中的指針指向的最小值賦值給索引號爲k1的,次小值賦值給索引號爲k2的
for (int j = k2; j < max; j++){
if(ptrArr[j] != NULL){
if(ptrArr[j]->data < ptrArr[k1]->data){
k2 = k1;
k1 = j;
}else if(ptrArr[j]->data < ptrArr[k2]->data){
k2 = j;
}
}
}
//由最小權值樹和次最小權值樹建立一棵新樹,pRoot指向樹根結點
pRoot = (PtrHuffman)malloc(sizeof(HuffmanTreeNode));
pRoot->data = ptrArr[k1]->data + ptrArr[k2]->data;
pRoot->left = ptrArr[k1];
pRoot->right = ptrArr[k2];
ptrArr[k1] = pRoot; //將指向新樹的指針賦給ptrArr指針數組中k1位置
ptrArr[k2] = NULL; //k2位置爲空
}
return pRoot;
}
ElemType calculateWeightLength(PtrHuffman ptrTree,int len){
if(ptrTree==NULL){ //空樹返回0
return 0;
}else{
if(ptrTree->left==NULL && ptrTree->right==NULL){ //訪問到葉子節點
return ptrTree->data * len;
}else{
return calculateWeightLength(ptrTree->left,len+1) + calculateWeightLength(ptrTree->right,len+1); //向下遞歸計算
}
}
}
int main(){
ElemType arr[10001];
int max;
scanf("%d",&max);
int i=0;
while(i<max)
scanf("%d",&arr[i++]);
PtrHuffman pRoot = createHuffmanTree(arr,max); //返回指向哈夫曼樹根節點的指針
printf("%d",calculateWeightLength(pRoot,0));
return 0;
}