五大算法思想:
一 贪心算法/贪婪算法
A星寻路
大问题 归纳成小问题 然后迭代,每次迭代都是解决小问题,经过无数次迭代后解决大问题
背包问题 最佳路径
能且只能做当前看来最佳的选择 如此反复 直至最终得到最优解
缺陷:
并非一定能得到整体最优解
每一步都是局部最优
花的时间一般比较少,八九不离十(整体并非最优,但也不差)
贪心算法准则:最值思想(最大 最小)
背包问题:
给定N种物品 一个背包
每个物品有其价值和体积
求 背包能装下的最具价值的解
总共有 n 元,有 m 种商品,每种商品有它的价值和价格
求 购买价值最高的解
具体如何解决:
对整个背包来说可以放很多种物品
对每一种物品来说 第 i 个 只有两种 (装和不装)
先把单位体积最高价值的放进去,再一个个放,每放一次判断一次 把放很多个 变成 放还是不放
求最短路径:迪杰斯卡拉算法 求 A 点到 B 点的最佳路径
某个点到终点的最短路径
无限覆盖 每次的访问路线都有很多种选择,每次把价值低的选择(路径长度比较大的)去掉,只考虑价值高的选择(路径长度比较小的)
二 分治思想
二分查找 归并排序 快排 23树
三 动态规划
四 动态回溯
五 分支定界
例子:
9
4 7
5 3 1
2 4 4 1
7 5 3 2 4
7 5 3 2 4
9 9 7 5
14 12 8
18 19
28
求从 顶 到 底 的最大路径 和
9 7 3 4 5 最大 和为 28
怎么用贪心算法的思想去算?
思路一:爆破法 递归 每一种可能都算一下 最终记录最大的
用递归的方式从上到下,每一步找最大的,最终把 值 返回
中间的每一步怎么计算?
1.计算 9 + 4 和 9 + 7 哪个更大,返回大的那个
2.计算 4 + 5 和 4 + 3 和 7 + 3 和 7 + 1 哪个更大
3.继续递归计算
4.最终返回最大的那一个
注意:
如果越界了,当前位置就是最后一个位置,如果没有越界就把大的那个加上
每次递归下面的两个都是 i + 1,j 不变 和 i + 1,j+1
存在浪费的情况
省略掉一些不必要的步骤:
在 Step1 中已经知道 7 + 2 == 9 > 2 + 5 == 7,但是依旧计算了一次 2 + 5 == 7,每个分支都计算了一遍,Step2 对此情况做优化,算过的部分不再计算
准备一个临时的数组做存储
代码实现
#include <stdio.h>
//5行5列的数组
#define NUM 5
int arr[NUM][NUM] = { 0 };
//记录最大路径和
int count = 0;
//临时数组 用来记录过程中的数据
int maxArr[NUM][NUM] = { 0 };
//初始化数据
void initArr();
//返回ab中大的那个-> 封装函数针对其他类型的比较
int Max(int a, int b);
//获取最大的 行->外层循环 列->内层循环
int getMax(int i, int j);
int main(){
initArr();
int num;
num = getMax(0, 0);
printf("%d,%d\n", num,count);
while (1);
return 0;
}
void initArr(){
arr[0][0] = 9;
arr[1][0] = 4; arr[1][1] = 7;
arr[2][0] = 5; arr[2][1] = 3; arr[2][2] = 1;
arr[3][0] = 2; arr[3][1] = 4; arr[3][2] = 4; arr[3][3] = 1;
arr[4][0] = 7; arr[4][1] = 5; arr[4][2] = 3; arr[4][3] = 2; arr[4][4] = 4;
for (int i = 0; i < NUM; i++){
for (int j = 0; j < NUM; j++){
maxArr[i][j] = -1; //临时数组初始化为-1-> -1说明这个位置上的点没有用过
}
}
}
#if 1
int getMax(int i, int j){
//step 4: 在step3的基础上优化临时数组 并不需要太大的临时数组 第2层的数据可以直接覆盖第1层
int temp[NUM];
//先给最下面一层赋值
for (int i = 0; i < NUM; i++) {
temp[i] = arr[NUM - 1][i];
}
//然后循环计算,一层层往上赋值
for (int i = NUM - 2; i >= 0; i--){//从下往上
for (int j = 0; j <= i; j++){//从左往右
temp[j] = arr[i][j] + Max(temp[j], temp[j + 1]);
}
}
//最终 temp[0]就是我们需要的结果
return temp[0];
}
#endif
#if 0
int getMax(int i, int j){
//step 3: 在step2的基础上把递归变成循环 7 5 3 2 4
//先给最下面一层赋值
for (int i = 0; i < NUM; i++) {
maxArr[NUM - 1][i] = arr[NUM - 1][i];
}
//然后循环计算,一层层往上赋值
for (int i = NUM - 2; i >= 0; i--){//从下往上
for (int j = 0; j <= i; j++){//从左往右
//加上相邻两个中间大的那一个
maxArr[i][j] = arr[i][j] + Max(maxArr[i + 1][j], maxArr[i + 1][j + 1]);
}
}
//最终 maxArr[0][0]就是我们需要的结果
return maxArr[0][0]; //28 15
}
#endif
#if 0
int getMax(int i, int j){
//step 2: step1有一些无意义的递归 可以省略 之前递归计算出的结果 直接存储 直接使用 不再算了
if (maxArr[i][j] != -1) return maxArr[i][j]; //一开始都是-1不会返回 后面不等于-1 直接返回对应位置的值
count++;
if (NUM == i){//从最后一行开始赋值
maxArr[i][j] = arr[i][j];
}
else{//递归
int n = getMax(i + 1, j);
int m = getMax(i + 1, j + 1);
maxArr[i][j] = arr[i][j] + Max(n, m);
}
return maxArr[i][j]; //28 15
}
#endif
#if 0
int getMax(int i, int j){
//step 1: 递归方式 每一步都计算
if (NUM == i) return arr[i][j]; //越界就结束-> 返回当前位置
int n = getMax(i + 1, j); //左边的分支
int m = getMax(i + 1, j + 1); //右边的分支
printf("i:%d,j:%d,n:%d,m:%d,arr[%d][%d]:%d\n",
i, j, n, m, i, j, arr[i][j]);
count++;
return arr[i][j] + Max(n, m); //返回当前位置加上n和m中间比较大的那个 28 31
}
#endif
//返回ab中大的那个
int Max(int a, int b){
return ((a > b) ? a : b);
}
贪心算法解决背包问题
先找最大价值的物品,一直存储,直到放不下
#include <stdio.h>
/*
有N样物品 和一个容量为V的背包
每种物品的体积w和价值c如下
N:5 V:20
A B C D E
w: 3 5 6 7 9
c: 2 8 7 4 1
求背包中价值最大的情况:32
*/
//容量
#define V 20
//体积
#define N 5
//结构体数组
struct WuPin{
int w, c;
};
WuPin wp[N] = { { 3, 2 }, { 5, 8 }, { 6, 7 }, { 7, 4 }, { 9, 1 } };
//返回ab中大的那个
int Max(int a, int b){
return ((a > b) ? a : b);
}
int main(){
int temp[100] = { 0 };
for (int i = 0; i < N; i++){//种类搭配 5种物品
//{ 3, 2 }, { 5, 8 }, { 6, 7 }, { 7, 4 }, { 9, 1 }
for (int j = wp[i].w; j <= V; j++){//只考虑体积w的变化 j增加的是体积 但temp只存价值
printf("temp[%d]:%d\n", j, temp[j]);
temp[j] = Max(temp[j], temp[j - wp[i].w] + wp[i].c);
printf("i:%d\t j:%d\t temp[%d]:%d\t temp[%d]:%d \t temp[%d] + %d\n",
i, j, j, temp[j], j - wp[i].w, temp[j - wp[i].w], j - wp[i].w, wp[i].c);
}
}
for (int i = 0; i < 100; i++)
printf("%d ", temp[i]);
printf("\n");
printf("max:%d\n", temp[V]);
while (1);
return 0;
}
0 0 0 2 2 8 8 8 10 10 16 16 16 18 18 24 24 24 26 26 32
max:32