演算法 演算法的應用(一)

2022-05-06 18:09:14 字數 4572 閱讀 8480

儲油問題

一輛重型卡車的油耗是1l/km,載油能力為500l,今欲穿過1000km的沙漠。由於卡車一次過不了沙漠,因此司機必須在沿途設幾個儲油點。問:如何建立這些儲油點,每乙個儲油點儲存多少油才能使卡車以最小油耗通過沙漠?

例項解析:

本例採用倒推法來解題。所謂倒推法,就是在不知初始值的情況下,通過某種遞推關係,由最終值推算出初始值的方法。儲油問題和猴子吃桃子問題等都是典型的倒退問題。

顯然,卡車要通過沙漠,必須在離起點500km處儲存500l油,如圖17-1所示。

圖17-1 儲油點及儲油量示意圖

而要在500km處儲油500l,需要卡車從某處(設離起點x1公里處)向500km處運送n趟(最後一趟不需要返回),卡車往返總耗油是:(2n-1)*(500-x1)*1,因此,x1處儲油量y1應是:y1 = (2n -1)*(500 -x1) +500,而這個儲油量也是卡車n趟的總載油量,即:y1=500n。

可以證明,卡車往返的總耗油 (2n-1)*(500-x1) =500時,最為省油。故:y1=500+500 =1000,此時n = 2,即x1處要儲油1000l,其中的500l送往中點500km處,另外500公里用來跑路(3個單程)。可以算出,x1的值為333km

同樣的道理,要在333km處儲油1000l,需要在前面某點x2處儲油500l*3=1500l,其中500l用來跑路(耗油500l最省油),另外1000l運送到333km處,即:y2 = (2*3 -1)*(x1-x2)+500*2 = 1500。

......

可以得出乙個通用公式:

某儲油點的儲油量應為:

yk = (2*(k+1) -1)*(xk-1-xk) + 500*k = 500*(k+1)

初始值:x0 = 500, k = 1。

我們可以定義乙個陣列distance[n]用來儲存各儲油點離起點的距離,其中distance[0] = 500,定義乙個陣列oil[n]儲存儲油量,oil[0] = 500。其他儲油點的座標和儲油量資料由上面的通用公式求得,即:

xk = x k-1-500/(2k+1)

yk = 500*(k+1)

注意一點:推導的過程中,xk的值越來越小,當某次計算出的座標xk<=0時,意味著已經計算到起點了。若xk = 0,意味著計算出的最經濟的儲油點正好位於起點,儲油量按照上面的通用公式計算即可,即yk=500*(k+1)。但若xk<0,意味著計算出的儲油點是不實際的,因此實際儲油量不需要公式計算的那麼多,所以要根據實際距離重新計算。

下面是程式**:

#include#define n 10

int main()

, oil[n] = ;

int k;

for(k = 1; distance[k-1] > 0; k++)

k--;

if(distance[k] < 0)

printf("\ndistance oil\n\n");

for( ; k >= 0; k--)

printf("%5d,%8d\n", distance[k], oil[k]);

getch();

return 0;

}

揹包問題

給定n種物品和乙個揹包,其中物品i的重量為wi,對應的價值為vi,揹包可裝物品的最大重量為c,問題:怎樣選擇物品,才能使裝入揹包的物品的價值最大?

例項解析:

在選擇物品時,每個物品的選擇都只有兩種:選或者不選,因此這個問題叫做0-1揹包問題。

我們採用動態規劃的方法來解決這個問題。其基本思想是:

將待解決的問題分成若干個子問題,先求子問題的解,然後從子問題的解中得出整個問題的解。

適用於動態規劃的問題經分解後形成的子問題往往不是相互獨立的,在求解過程中如果能儲存已解決的子問題的答案,以便在需要時加以利用,就可以避免大量重複計算。為了達到這個目的,可以用乙個表來記錄所有已解決的子問題的答案(不管有用無用,都儲存),這就是動態規劃的主要思想。

本例中,我們定義三個陣列來描述揹包問題:

int value[n];          //用來儲存各物品的價值

int weight[n]; //用來儲存各物品的重量

int maxvalue[n][content]; //儲存最優解

陣列maxvalue用來儲存動態規劃過程中的最優解。例如:maxvalue[i][j]表示揹包剩餘容量為j,還有第i,i+1,i+2,......n-1物品可選擇時的最優解。

程式**如下:

#define  content 5

#define n 10

#include void knapsack(int v[n],int w[n], int c, int m[n][content+1])}}

void traceback(int flag[n], int w[n], int m[n][content+1])

if(m[n][c]>0) //判斷最後一種物品選擇與否

flag[n] = 1;

}void printresult(int flag, int w, int v, int m[content+1])

int main()

;int weight[n] = ;

int c = content;

int maxvalue[n][content+1];

int flag[n] = ;

clrscr();

knapsack(value, weight, c, maxvalue);

traceback(flag, weight, maxvalue);

printresult(flag, weight, value, maxvalue);

getch();

return 0;

}

鍊錶的逆置

程式設計實現鍊錶(無頭結點)的逆置。

例項解析:

所謂鍊錶逆置,就是將所有結點逆序排列。本例可用兩種方法來做。

方法一:從第乙個結點開始直到鏈尾,依次處理每乙個結點。

(1)對於第乙個結點,其指標域next賦值為null,使之成為鏈尾。

(2)對於之後的結點,使之指向前一結點。

(3)最後乙個結點的指標,賦給head,使之成為第乙個結點。

下面是逆置部分的程式**:

struct student* reverse(struct student *head)

head = p2; //最後乙個結點成為第乙個結點

return head;

}

方法二:

從鍊錶第二個結點開始,依次將每個結點插入到鍊錶的最前面。

相應的函式**是:

struct student* reverse(struct student *head)

h->next = null; //最早的第乙個結點成為鏈尾

}return head;

}

約瑟夫環

n個小孩圍成一圈,從第乙個人開始報數,報到k的人退出圈子,下面的人繼續從1開始報數.....若最後圈子裡只剩下m個人,他們分別是多少號?  n、k、m都從鍵盤輸入。      

例項解析:

這是乙個典型的單迴圈鍊錶問題。先建立鍊錶,然後從第乙個結點開始計數,將第k個結點刪除,然後再從下一結點開始計數,第k個結點刪除......,直到鍊錶中只剩m個結點。

下面是程式**:

#include "stdio.h"

#include "stdlib.h"

struct boy ;

void create(struct boy**, int);

struct boy* proceed(struct boy*, int, int, int);

void print(struct boy*);

void del(struct boy*);

int main()

void create(struct boy **p,int n)

p1->n = i;

if(i == 1)

*p = p1;

else

p2->next = p1;

p2 = p1;

p1->next = *p;}}

void print(struct boy *head)

}struct boy* proceed(struct boy *head,int n,int k,int m)

p2->next = p1->next;

p2 = p1; //p2指向要刪結點

p1 = p1->next;

free(p2);

}head = p1;

//以下**使head指向序號最小的結點,以便按從小到大順序輸出結果

min = p1->n;

for(i = 1; i <= m; i++)

}return head;

}//刪除整個鍊錶

void del(struct boy *head)

}

本文出自 「成鵬致遠」 部落格,請務必保留此出處

kosaraju演算法應用(一)

poj 2186 解題思路 kosaraju演算法,本以為要縮點,但是題目只要求找到拓撲排序的乙個唯一的頭,可以水過 通過計算強連通分量的出度。include include include include include using namespace std int n,m,v 10005 nu...

遞迴演算法的應用

提起漢諾塔,大家都會想起遞迴程式,大家都知道遞迴程式的實現是用棧來實現的,但是,有些程式是需要用到棧,但是我們還要編寫一棧的資料結構,挺麻煩的,所以,用遞迴程式實現起來是很簡單的!1.學習資料結構時,講到迷宮演算法,是用棧實現的,如果用遞迴演算法實現會更簡單的.掃雷程式也是實行遞迴搜尋的.對於迷宮程...

排序演算法的應用

假設序列中有n個元素,取其前k個組成乙個最大堆。由於最大堆的堆頂為序列中最大元素,所以組成的最大堆的堆頂是前k個元素中最大的元素。依次用第k 1到第n個元素與堆頂進行比較,如果比堆頂元素大,那麼該元素肯定不會是前k個最小的元素 如果比堆頂小,那麼堆頂的元素肯定不是前k個最小的元素,此時更新堆頂元素,...