贪心算法与动态规划

贪心算法

在这里插入图片描述

硬币找零

例如,有以下面额(硬币):1,5,10,5。 如果要找36的零钱,我们可以用1个25的硬币、1个10的硬币和1个1的硬币。 如何将这个解答转化成算法?

function get_min_coins(price) {
  const coins = [100, 25, 10, 5, 1];
  let coins_list = [];
  for (let i = 0; i < coins.length; i++) {
    let coins_count = parseInt(price / coins[i]); //面值个数
    coins_list.push(coins_count + "张" + coins[i]);
    price = price - coins_count * coins[i];
    if (price <= 0) {
      break;
    }
  }
  return coins_list;
}
const res = get_min_coins(135);
console.log(res);

间隔任务规划

问题描述
• 输⼊为 n 个报告集 R=[r1, …… , rn],以及每⼀个报 告的开始不结束时间 ri=[ai, bi]
• 输出:最多的相容报告集
• 两个报告相容:即两报告的发⽣时间⾥⾯没有重合
贪心策略:每次选择结束时间最早的报告

function get_max(jobList) {
  const job_schedule = [];//可观看会议
  const num_job = jobList.length;//总会议长度
  //将所有会议按照结束时间进行升序排序,二维数组排序
  jobList = jobList.sort(function (x, y) {
    return x[2] - y[2];
  });
  //遍历所有会议
  for (let i = 0; i < num_job; i++) {
    if (!job_schedule.length) {//如果当前job_schedule数组中没有任务,直接加进去
      job_schedule.push(jobList[i]);
    } else {
      //找出与job_schedule中最后一个jobs相容的
      if (job_schedule[job_schedule.length - 1][2] <= jobList[i][1]) {
        //当前job_schedule中最后一个会议的结束时间 <= jobList中会议的开始时间
        job_schedule.push(jobList[i]);
      }
    }
  }
  return job_schedule;
}
var jobList = [
  ["e", 8, 10],
  ["b", 2, 5],
  ["c", 4, 7],
  ["a", 1, 3],
  ["d", 6, 9],
];
var res = get_max(jobList);
console.log(res);

动态规划

斐波那契数

既然算法的实现过程存在诸多重复的函数调用,那么为了提高算法执行效率,应该
虑优化这些重复的函数调用。
我们采用的方法非常简单,记忆。
在计算出一个输入参数为n的斐波那契数fib(n)后,就把fib(n)用表的形式存储下来。在函数递归调用前,先查表中是否有对应值,有则直接使用;如果表中没有,调用该参数对应的递归函数;

function fib(n) {
  let memo = {};
  const fib2 = (n) => {
    let f;
    if (memo[n]) {
      return memo[n];
    } else {
      if (n < 2) {
        f = 1;
      } else {
        f = fib(n - 1) + fib(n - 2);
      }
      memo[n] = f;
      return f;
    }
  };
  return fib2(n);
}
const res = fib(5);
console.log(res);

递归调用

function fib(n) {
  if (n < 2) {
    return 1;
  } else {
    return fib(n - 1) + fib(n - 2);
  }
}
const res = fib(5);
console.log(res);

一维动态规划

拾捡硬币

假设有n个硬币排在一行,要求不能拾取相邻的两个硬币,已获得累加面值最大的拾取子序列
对于第i个硬币:
1)拾取第i个硬币,则table[i-2]+c[i]
2)不拾取第i个硬币,则table[i-1]
取两者里边的最大值给了table[i]
在这里插入图片描述
在这里插入图片描述

function max(arr) {
  let table = {};
  table[0] = 0;
  table[1] = arr[0];
  for (let i = 2; i < arr.length + 1; i++) {
    table[i] = Math.max(table[i - 2] + arr[i - 1], table[i - 1]);
  }
  return table;
}
const arr = [5, 1, 2, 10, 6, 2];
const res = max(arr);
console.log(res);

连续子序列和的最大值

做法跟上面的类似
重点:前n项和要大于0,若小于0,重新累加
递归表达式:
在这里插入图片描述

function max(arr) {
  let table = {};//建立一个空表
  table[0] = arr[0];//初始条件
  for (let i = 1; i < arr.length; i++) {//遍历数组剩下元素
    table[i] = Math.max(table[i - 1] + arr[i], arr[i]);//取累加最大值,并保存起来
  }
  return table;
}
let arr = [-2, 11, -4, 13, -5, 2];
const result = max(arr);
console.log(result);

0-1背包问题

问题描述:
给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装
入背包中物品的总价值最大?
对于一种物品,要么装入背包,要么不装。所以对于一种物品的装入状态可以取0和1.我们设物品i的装入状态为xi,xi∈ (0,1),此问题称为0-11背包问题。
过程分析:
数据:物品个数n=5,物品重量w[n]={0,2,2,6,5,4},物品价值V[n]={0,6,3,5,4,6},
(第0位,置为0,不参与计算,只是便于与后面的下标进行统一,无特别用处,也可不这么处理。)总重量c=10.
背包的最大容量为10,那么在设置数组m大小时,可以设行列值为6和11,那么,对于m(i,j)就表示可选物品为i…n背包容量为j(总重量)时背包中所放物品的最大价值。

var have = 4; // 背包最大容量
var n = 3; // 代表有5个物品
var weight = [1, 2, 3]; //代表5个物品的重量
var price = [1500, 2000, 3000]; //代表5个物品的价值
function knap(have, weight, price, n) {
  let k = [];
  for (let x = 0; x < n + 1; x++) {
    k.push([]);
  }
  for (let i = 0; i < n + 1; i++) {
    for (let w = 0; w < have + 1; w++) {
      if (i == 0 || w == 0) {
        k[i][w] = 0;
      } else if (weight[i - 1] <= w) {
        k[i][w] = Math.max(
          price[i - 1] + k[i - 1][w - weight[i - 1]],
          k[i - 1][w]
        );
      } else {
        k[i][w] = k[i - 1][w];
      }
    }
  }
  return k;
}
let res = knap(have, weight, price, n);
console.log(res);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值