算法思想(持续更新...)
文章目录
- 二分法
- 递归
- 分而治之(D&C,divide and conquer)
- 贪婪算法
- 背包问题
- 集合覆盖问题
- NP完全问题
- 动态规划
- 再探背包问题
- 细节补充
二分法
参照二分查找。
递归
递归通俗来讲就是不断调用自身。
递归只是让解决方案更加清晰,并没有性能上的优势。
如果使用循环,程序的性能可能更高;如果使用递归,程序可能更容易理解。
递归有三个基本组成部分,分别是执行的内容(可省略)、基线条件(函数不再调用自己)以及递归条件(函数调用自己)。
int factorial(int x){// 基线条件if (x == 1)return x;// 递归条件elsereturn x * fab(x);
}
递归在使用过程中可能占用大量的内存(调用栈),此时有两种解决方案:
- 重新编写代码,转而使用循环;
- 使用尾递归。
分而治之(D&C,divide and conquer)
分而治之策略是递归的,包括了两个过程:
- 找到基线条件,这种条件必须尽可能简单;
- 不断将问题分解(或者说缩小规模),直到符合基线条件。
分而治之是基于递归的,更是一种解决问题的思想,递归偏于解决问题的策略/手段。
以对数组中的数求和为例:
首先,我们需要找到基线条件:数组为空时返回0或者数组只有一个元素时返回该元素;
接着,我们需要对问题进行分解,即每一次递归都将数组的最后一个元素+sum(剩下的元素构成的数组)。因为,传递给sum的数组变得简单了,这也是一种问题规模的缩减。
int sum(vector<int> seq){if seq.empty()return 0;else if (seq.size == 1)element = seq.back();seq.push_back();return element + sum(seq);
}
贪婪算法
贪婪算法的本质:每步都选择局部最优解,最终得到的就是全局最优解。
并非在任何情况下都行之有效。
背包问题
有一个小偷,只偷最贵的东西,但是他的包只能装下35磅的物品。
此时,在他眼里,有三样物品:
物品 | 重量/kg | 价格/¥ |
---|---|---|
音响 | 30 | 3000 |
笔记本电脑 | 20 | 2000 |
吉他 | 15 | 1500 |
显而易见,按照贪婪算法的要求,小偷无法使自己的利益最大化,即得不到最优解。
集合覆盖问题
问题是:一共有八个州,四个电台,希望能选择尽可能少的电台将这八个周覆盖了。
4个电台就有242^424个集合需要考虑,那么枚举的话,时间为O(2n)O(2^n)O(2n)。
为了加快解题速度,解题思路是:每次都选择覆盖多的。
这是一种近似算法,得到近似解。
#include <iostream>
#include <map>
#include <string>
#include <set>
#include <algorithm>
#include <vector>
using namespace std;int main() {// 电台信息map<string,vector<string> > stations;stations["kone"] = { "id", "nv", "ut" };stations["ktwo"] = { "wa", "id", "mt" };stations["kthree"] = { "or", "nv", "ca" };stations["kfour"] = { "nv", "ut" };stations["kfive"] = { "ca", "az" };// 初始化// 当前未覆盖的州vector<string> states_needed = { "mt", "wa", "or", "id", "nv", "ut", "ca", "az"};// 候选电台列表vector<string> final_stations;// 选中电台string best_station;// 选中电台所覆盖的州vector<string> states_covered;// 贪心算法while (!states_needed.empty()) {// 遍历电台,找最优for (auto& s : stations) {// 候选电台与未覆盖州的交集vector<string> covered;sort(states_needed.begin(), states_needed.end());sort(s.second.begin(), s.second.end());set_intersection(states_needed.begin(), states_needed.end(), s.second.begin(), s.second.end(), back_inserter(covered));if (covered.size() > states_covered.size()) {best_station = s.first;states_covered = covered;}}// 记录选中电台final_stations.push_back(best_station);// 删除选中电台stations.erase(best_station);// 删除已经覆盖的州set<string> states_needed_set(states_needed.begin(), states_needed.end());for (auto& s : states_covered)states_needed_set.erase(s);states_needed.assign(states_needed_set.begin(), states_needed_set.end());// 重置best_station = {};states_covered = {};}for (auto& s : final_stations)cout << s << endl;return 0;
}
在写代码的过程中,有以下几个注意点:
- 一些参与比较,时常更新的值要注意是否每个循环都要初始化;
- 使用交集算法
set_intersection
时,需要注意的有:将两个集合排序;通过push_back插入结果,不是赋值;选取支持push_back的容器。
NP完全问题
没有办法判断问题是不是NP完全问题(没有快速算法的问题),但是是有一些蛛丝马迹可循的:
- 元素较少时算法的运行速度非常快,但随着元素数量的增加,速度会变的非常慢;
- 涉及“所有组合”的问题通常是NP完全问题;
- 不能将问题分成小问题,必须考虑各种可能的情况。这可能是NP完全问题;
- 如果问题可转化为集合覆盖问题或旅行商问题,则肯定是NP完全问题。
NP完全问题可以通过近似算法得到近似解。
动态规划
再探背包问题
动态规划问题和NP完全问题的目的都是为了找一个最优解,但区别在于,动态规划问题看似是个NP问题,但是其可以分解成小问题。
比如上文我们描述的背包问题。
我们可以先解决子背包问题,再逐步解决原来的问题。
我们设背包承重4磅。吉他1磅,1500美金;音响4磅,3000美金;笔记本电脑3磅,2000美金。
我们首先给出一个网格:
1 | 2 | 3 | 4 | |
---|---|---|---|---|
吉他 | ||||
音响 | ||||
笔记本电脑 |
从左到右,表示逐步扩大的背包承重数;从上到下,表示可供选择的物品的增加。
每一格的含义是,在x的承重下,从y(该行及往上的物品)中偷盗物品可以获得的最大金额。
于是,这张表格可以写成:
1 | 2 | 3 | 4 | |
---|---|---|---|---|
吉他 | 1500 | 1500 | 1500 | 1500 |
音响 | 1500 | 1500 | 1500 | 3000 |
笔记本电脑 | 1500 | 1500 | 2000 | 3500 |
该表格的公式为(i表示行,j表示列):
CELL[i][j]=max(CELL[i−1][j](上一个单元格的值),当前商品的价值+CELL[i−1][j−当前商品的重量])CELL[i][j]=\max(CELL[i-1][j](上一个单元格的值),当前商品的价值+CELL[i-1][j-当前商品的重量])CELL[i][j]=max(CELL[i−1][j](上一个单元格的值),当前商品的价值+CELL[i−1][j−当前商品的重量])
这其实就是一个有约束条件的优化问题,约束条件为背包承重,优化对象为偷盗的钱财。
细节补充
- 如果再增加一个物品,不过是给这个表格再加一行之后套公式求解;
- 如果补充了一件0.5磅的物品,那么考虑的粒度更细,我们网格也要更细;
- 对于商品,只能一整件,不能说偷商品的百分之多少;
- 每个子问题都是离散的,不存在依赖关系。
设计动态规划模型时的注意:
- 每种动态规划都涉及网络;
- 单元格中的值通常是我要优化的值,
- 每个单元格都是一个子问题,因此你应考虑如何将问题分成子问题。
计算编辑距离的过程也是动态规划的思路!
算法思想(持续更新...)相关推荐
- IRS中波束赋形设计源代码之AO算法学习(持续更新,多多交流)
IRS中波束赋形设计源代码之AO算法学习(持续更新,多多交流) 论文:Weighted Sum-Rate Maximization for Reconfigurable Intelligent Sur ...
- 目标跟踪经典算法汇总(持续更新...)
如题,虽然这个问题是经典目标跟踪算法,但事实上,可能我们并不需要那些曾经辉煌但已被拍在沙滩上的tracker(目标跟踪算法),而是那些即将成为经典的,或者就目前来说最好用.速度和性能都看的过去trac ...
- 回溯算法(持续更新)
回溯算法的核心思想:回溯法是深度优先遍历中的一种特有现象,主要用于在一个较大的数据集中寻找满足特定条件的解.回溯法就是当前状态不满足条件时,就回到上一个状态,即回到过去,然后再次向下搜索.在此过程中, ...
- 算法-4-经典算法-汇总(持续更新)-目录呈现
经典算法主要是一些递归.分治.动归.回溯.贪心等算法,面试笔试要求高. 以下为超链接,点击即可 算法-Q-[第4章]-经典-[V]-思想-分治-递归.分治.动态规划.回溯=============== ...
- hadoop常见算法(持续更新)
1. 对以下数据进行排序,根据收入减去支出得到最后结余从大到小排序 账号 收入 支出 日期 zhangsan@163.com 6000 0 2014-02-20 lisi@163.com 2000 0 ...
- javascript算法汇总(持续更新中)
1. 线性查找 1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset ...
- 图像增强算法(持续更新中)
1.空间域增强 1)点运算 a)线性变换 b)非线性变换 c)直方图均衡化 d)直方图规定化 2) 邻域运算 a)图像平滑 b)图像锐化 2.频率域增强 1)图像平滑 2)图像锐化 3.彩色增强 1) ...
- 快来看JS的的几个常用算法(持续更新中)
数组去重 // 第一种方法let arr = [1,1,2,3,4];function fun(v) {return Array.from(new Set(v))}console.log(fun(ar ...
- 本专栏所有力扣题目的目录链接, 刷算法题目的顺序(由易到难/面试频率)/注意点/技巧, 以及思维导图源文件问题(持续更新中)
这篇文章为本专栏所有力扣题目提供目录链接, 更加方便读者根据题型或面试频率进行阅读, 此外也会介绍我在刷题过程中总结的刷算法题目的顺序/注意点/技巧, 最后说下文中出现的思维导图源文件的问题 和 打卡 ...
- 最值得收藏的 数字图像处理 全部知识点思维导图整理(武汉大学慕课课程)(持续更新中)
本文的思维导图根据慕课上的武汉大学数字图像处理国家精品课程整理而来并标记出重点内容 思维导图就整理了这么多,之后应该也不会更新此内容了, 有需要的可以去 我的主页 了解更多学科的精品思维导图整理 本文 ...
最新文章
- golang 打印函数名/文件名/行号 Callers
- linux 网络7层模型,Linux网络编程——OSI七层模型、TCP/IP模型
- 2013 Multi-University Training Contest 9 1011 Arc of Dream
- batocera游戏整合包_星露谷物语绅士mod整合包
- linux去掉u盘写保护,最全面win10系统下u盘写保护怎么去掉
- 一文带你看完ZooKeeper!
- Debian wheezy安装Redis 3.0
- 13. Roman to Integer
- hive学习2(Navicat连接hive)
- EasyUI:Layout 布局
- opencv 识别火灾_使用深度学习和OpenCV早期火灾探测系统
- 小鱼易连手机显示无法连接服务器,小鱼易连怎么投屏到电视 小鱼易连app手机无线投屏使用方法...
- LaTex实战笔记 3-宏包与控制命令
- Linux下C语言编程资料
- 清华大学计算机与科学系张荷花简历,清华大学软件学院
- 恕我直言,赚钱真的不是靠拼命
- Sql Server服务远程过程调用失败。[0x800706be]
- 数字证书连接服务器异常,连接时Socket.io + SSL +自签名CA证书出现错误
- Linux的ip设置
- 2021年R2移动式压力容器充装考试技巧及R2移动式压力容器充装模拟考试题