#include <stdio.h> #include <stdlib.h>
#define MaxNum 500
int PeopleNeeded[MaxNum]; //PeopleNeeded[i]表示第i座金礦須要的開採人數
int Gold[MaxNum]; //Gold[i]表示第i座金礦開採後獲得的金子數目
int goldGet(int People, int mineNum) { if(mineNum == 0) { /*考慮最後一座金礦是,若是剩餘開採金礦的總人數 多餘或者等於須要的人數,則返回該金礦的金子數 目,不然返回0。*/
if(People >= PeopleNeeded[mineNum]) { return Gold[mineNum]; } else { return 0; } } else { int m, n; /*考慮第mineNum座金礦,第一種狀況:開採該座金礦所能得到的最多的金子數目, 爲該座金礦的金子數目和剩下的人開採剩下的金礦所獲得的最多的金子數目的和 第二種狀況:部開採該座金礦所得到的最多的金子數目,是目前全部的人用來開 採剩餘的金礦得到的最多的金子數目。返回兩種狀況下金子數目較多的一種狀況 的金子數目。*/
if(People >= PeopleNeeded[mineNum]) { m = Gold[mineNum] + goldGet(People - PeopleNeeded[mineNum], mineNum - 1); } else { m = goldGet(People, mineNum - 1); } n = goldGet(People, mineNum - 1); return m >= n ? m : n; } } int main() { int People; //開採金礦的總人數
int GoldNum; //金礦數目
int GoldGet; //最終可以獲取的金子的總數
int i, j; //用來進行的便利的變量
scanf("%d %d", &GoldNum, &People); for(i = 0; i < GoldNum; i ++) { scanf("%d %d", &PeopleNeeded[i], &Gold[i]); } GoldGet = goldGet(People, GoldNum); printf("%d\n", GoldGet); return 0; }
/*01揹包問題*/ #include <stdio.h> #include <stdlib.h> #include <memory.h>
int max(int x, int y) { return x >=y ? x : y; } int main() { int N; //表示獎品的個數
int M; //表示獎券數
int need[500]; //need[i]表示第i個獎品須要的獎券數
int value[500]; //value[i]表示第i個獎品的評分值
int best[100000]; //best[j]表示對於i個獎品時j張獎券的最大評分值
memset(best, 0, sizeof(best)); //初始化best的全部元素爲0
int i, j; //程序進行遍歷的變量
scanf("%d %d", &N, &M); for(i = 0; i < N; i ++) { scanf("%d %d", &need[i], &value[i]); } for(i = 0; i < N; i ++) { for(j = M - 1; j >= need[i]; j --) { best[j] = max(best[j], best[j - need[i]] + value[i]); } } printf("%d\n", best[M - 1]); return 0; }
嗯。。。這裏代碼貌似要斷了不少,對於前面的輸入輸出你們都看得懂,主要的是最後一個循環,其實這個循環也比較好理解,在我貼的第一個比較low的程序裏面,轉移方程是用遞歸實現的,這裏改爲了雙重循環,實際上是一個意思。最難理解的是:
best[j] = max(best[j], best[j - need[i]] + value[i]);
爲了理解這句話須要一步步的來,將一開始比較low的遞歸搞成雙重循環(這裏不是說遞歸比較low,而是說我寫的比較low)
for(i = N- 1; i > 0; i --) { for(j = M - 1; j >= need[i]; j --) { best[i, j] = max(best[i - 1][ j], best[i - 1][j - need[i]] + value[i]); } }
這裏i > 0是由於i = 0沒有討論的必要,其實嚴格的來講這個循環和前面的遞歸併不徹底同樣,這裏的時間複雜度更低一些,由於這裏的最高收益是用數組進行存儲的,對於某次循環中計算出了best[x][y]的值後,後面若是還須要用到就不須要再計算了,而以前的遞歸用的是函數,並不能存儲數值,效率更低。
這裏咱們能夠在複習一下轉移方程,
當i != 0時 bes[i][j] 是 best[i - 1][j - need[i]] + value[i]與best[i - 1][j]中的較大者
這裏咱們能夠發現best[Ai][Aj]依賴於best[Bi][Bj]時,確定有Ai = Bi + 1,以及Aj >= Bj。
舉個簡單的例子,咱們求best[5][j]的時候只與best[4][j]和best[4][j - need[5]]有關,對於前面的best[0][k],best[1][k],...best[3][k],其中k = 0,1,...M-1沒有任何關係,那麼咱們就沒有必要用那麼複雜的存儲。而且j的取值是從M-1到0,考慮到Aj >= Bj的規律,計算best[i][j]的時候,best[i-1][j+1,...M-1]已經沒有任何利用價值了,這些空間也是多餘的,怎麼辦呢,其實計算best[i][j - 1]的時候best[i - 1][j]就沒有價值了,那麼咱們計算best[i][j]的結果就能夠直接存放到best[i - 1][j]的地址上,這樣的話,咱們的存儲其實只須要一個大小爲M的一維數組。而且是best[i][j]覆蓋beat[i - 1][j],因此循環的時候i要從0到N-1,
因而循環就能夠寫成
for(i = 0; i < N; i ++) { for(j = M - 1; j >= need[i]; j --) { best[j] = max(best[j], best[j - need[i]] + value[i]); } }
這個就是咱們最後的結果。