#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]); } }
这个就是咱们最后的结果。