mirror of
https://github.com/Estom/notes.git
synced 2026-04-04 03:17:44 +08:00
123
This commit is contained in:
47
code_segment/xiaohongshu1.cpp
Normal file
47
code_segment/xiaohongshu1.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include<bits/stdc++.h>
|
||||
using namespace std;
|
||||
/**
|
||||
* 交换任意两数的本质是改变了元素位置,
|
||||
* 故建立元素与其目标状态应放置位置的映射关系
|
||||
*/
|
||||
int getMinSwaps(vector<int> v)
|
||||
{
|
||||
vector<int> v1(v); //将A内元素复制到B。
|
||||
sort(v1.begin(), v1.end());
|
||||
map<int,int> m;
|
||||
int len = v.size();
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
m[v1[i]] = i; // 建立每个元素与其应放位置的映射关系
|
||||
}
|
||||
int loops = 0; // 循环节个数
|
||||
vector<bool> flag(len, false); //初始化
|
||||
//找出循环节的个数
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (!flag[i])
|
||||
{
|
||||
int j = i;
|
||||
while (!flag[j]) //对环处理
|
||||
{
|
||||
flag[j] = true;
|
||||
j = m[v[j]]; //原序列中j位置的元素在有序序列中的位置
|
||||
}
|
||||
loops++;
|
||||
}
|
||||
}
|
||||
return len - loops;
|
||||
}
|
||||
vector<int> v;
|
||||
int main()
|
||||
{
|
||||
int n,k;
|
||||
cin>>n;
|
||||
while(n--){
|
||||
cin>>k;
|
||||
v.push_back(k);
|
||||
}
|
||||
int num = getMinSwaps(v);
|
||||
cout<<"交换次数:"<<num<<endl;
|
||||
return 0;
|
||||
}
|
||||
0
code_segment/xiecheng1.cpp
Normal file
0
code_segment/xiecheng1.cpp
Normal file
@@ -26,6 +26,7 @@
|
||||
* 流程
|
||||
* [x] 拒绝了农银金科的笔试(说实话有点蠢,分行能进也不错了)
|
||||
* [x] 完成了简历投递
|
||||
* [ ] 农行笔试2021年9月17日18:00-20:30
|
||||
|
||||
### 建设银行
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* 8、对隐私计算(联邦学习、多方安全计算、机密计算)有了解优先。
|
||||
* 流程
|
||||
* [x] 简历投递与直通终面协商。https://jobs.bytedance.com/campus/position/application
|
||||
* [ ] 直通终面时间2021-09-13 15:30面试链接:https://people.toutiaocloud.com/hire/bridge/video/interviewee/fe3ffa33-19ab-4be9-aa9e-4bd1af49d05c
|
||||
* [x] 直通终面时间2021-09-13 15:30面试链接:https://people.toutiaocloud.com/hire/bridge/video/interviewee/fe3ffa33-19ab-4be9-aa9e-4bd1af49d05c
|
||||
|
||||
### 阿里
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
* [x] 百度一面2021-08-30 14:00-15:00:https://code.meideng.net/ykl1
|
||||
* [x] 建立共享当中,应该是面试挂掉了
|
||||
* [x] 新的笔试(都挂了,不应该啊)2021年-09-07 19:00:00 -- 21:00:00
|
||||
* [ ] 2021-09-11 16:00百度一面 9-11 16:00-17:00
|
||||
* [x] 2021-09-11 16:00百度一面 9-11 16:00-17:00
|
||||
|
||||
|
||||
|
||||
@@ -135,6 +135,8 @@
|
||||
|
||||
* 流程
|
||||
* [x] 官网投递https://campus.163.com/app/personal/apply
|
||||
* [ ] 网易笔试9月18日(周六)14:00,笔试时长预计3小时
|
||||
* [ ] 网易雷火2021-09-18 19:00-2021-09-18 21:00
|
||||
|
||||
### 搜狐
|
||||
* 岗位
|
||||
@@ -207,6 +209,7 @@
|
||||
|
||||
* 流程
|
||||
* [x] 简历投递2021-09-09
|
||||
* [ ] 快手一面:2021-09-18 11:00(GMT+08:00)
|
||||
|
||||
----
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
* [x] 标准库
|
||||
* [x] 面向对象
|
||||
* [x] 设计模式
|
||||
* [ ] 并发编程
|
||||
* [ ] 网络编程
|
||||
* [x] 并发编程
|
||||
* [x] 网络编程
|
||||
* [ ] GO
|
||||
* [x] 重新整理go知识(在原先的基础上进行了扩充)
|
||||
* [ ] 基础知识。
|
||||
@@ -54,7 +54,7 @@
|
||||
* [ ] 算法编程(力扣刷题)
|
||||
|
||||
### 简历准备
|
||||
* [ ] 对简历上的项目进行介绍
|
||||
* [x] 对简历上的项目进行介绍
|
||||
|
||||
## 计划
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
1. 关于封装的讨论。
|
||||
6
工作日志/2021年9月15日-今日计划.md
Normal file
6
工作日志/2021年9月15日-今日计划.md
Normal file
@@ -0,0 +1,6 @@
|
||||
2021年9月16日
|
||||
|
||||
1. 完成算法复习
|
||||
2. 做完携程的笔试
|
||||
3. 开始LeetCode刷题(每天30个)
|
||||
4. 开始面经复习(每天一门数据库、操作系统、计算机网络)
|
||||
@@ -82,12 +82,12 @@ $$
|
||||
* 头递归:递归发生在函数的其他处理代码之前(或理解为,递归发生在函数的头部或顶部)。**需要先解决后续问题,上层依赖下层,最顶层得到结果,直接返回**。
|
||||
* 需要使用后续递归后的结果,参与本层的计算,然后返回给上一层。这时,需要在下层的递归完成后,才能计算。如归并计算,需要下一层拍好顺序,这一次才会执行归并操作。
|
||||
* 尾递归:递归发生在函数其他处理代码的后面(或理解为,递归发生在函数的尾部)。**需要先解决当前问题,下层依赖上层,最底层得到结果,返回顶层**。
|
||||
* 本层内的计算是为了确定递归过程中的条件。且下一层返回的结果,能够作为本层的结果返回。本层不知道改成的结果,结果取决于最底层。如搜索树的最大深度,只有递归到叶节点才知道最大深度是多少。
|
||||
* 本层内的计算是为了确定递归过程中的条件。且下一层返回的结果,能够作为本层的结果返回。本层不知道改成的结果,结果取决于最底层。如搜索树的最大深度,只有递归到叶节点才知道最大深度是多少。快排也是,先解决本层的分割排序,在解决下一层的。
|
||||
* 中间部分递归:递归发生在函数体的中间部分。
|
||||
* 头递归和尾递归只是描述递归发生的相对顺序。实际上在递归过程中,递归可以发生在任何位置。递归前的代码负责处理本层的与下一层无关的内容。递归后的代码负责利用下一层的返回值或其他变量处理内容。
|
||||
|
||||
### 单分支递归和多分支递归
|
||||
* 单分支递归:每一层只是常数级别缩小问题的规模。进行一次递归处理下一个问题。每一层只**调用一次递归函数**。如阶乘问题青蛙跳问题等。
|
||||
* 单分支递归:每一层只是常数级别缩小问题的规模。进行一次递归处理下一个问题。每一层只**调用一次递归函数**。如阶乘问题青蛙跳问题等。单分支递归往往能写成循环的形式,如二分法的递归和循环都可以。
|
||||
* 多分支递归:当一个问题可以分为多个相同的子问题的时候。在同一层进行多分支递归。每一层**多次调用递归函数**。如树和图的遍历问题,有多个下层子节点。
|
||||
## 3 递归的实现
|
||||
|
||||
@@ -118,7 +118,7 @@ $$
|
||||
* 递归算法本质上是一种自顶向下的思考模式。即数学上所说的归纳法。有结果一步一步归纳,得到验证其条件的正确性。问题规模逐渐变小。
|
||||
* 非递归算法的本质是一种自底向上的思考模式。即数学上所说的推导法。将一些零碎的部分逐渐求解,渐渐得到最终的结果。问题逐渐组装成目标问题。
|
||||
|
||||
* 在使用递归的时候,应该从顶层考虑怎么分割。在使用非递归算法的时候,应该考虑怎样从顶层进行组合。
|
||||
* 在使用递归的时候,应该从顶层考虑怎么分割。在使用非递归算法的时候,应该考虑怎样从底层进行组合。
|
||||
|
||||
|
||||
## 4 迭代法概述
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
如果有向图具有一个有向的回路,该问题是无解的。因此,为了使得拓扑排序成为可能,问题中的图必须是一个无环有向图。
|
||||
|
||||
|
||||
### 减治法原理
|
||||
### 拓扑排序-减治原理
|
||||
|
||||
基于减(减一)治技术的一个直接实现:重复以下过程:
|
||||
|
||||
@@ -23,4 +23,165 @@
|
||||
2. 然后把该源和所有从它出发的边都删除。(如果有多个这样的源,可以任意选择一个;如果这样的源不存在,算法停止,因为该问题是无解的)
|
||||
3. 顶点被删除的次序就是拓扑排序的一个解。
|
||||
|
||||
```c++
|
||||
// 拓扑排序——循环入度为零
|
||||
vector<int> tuopu1(vector<vector<int>> vec){
|
||||
vector<int> res;
|
||||
vector<int> in(vec.size(),0);
|
||||
vector<int> flag(vec.size(),0);
|
||||
for(int i=0;i<vec.size();i++){
|
||||
for(int j=0;j<vec[i].size();j++){
|
||||
in[vec[i][j]]++;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i=0;i<vec.size();i++){
|
||||
int j;
|
||||
// 查找入度为零的节点,添加到结果中,并标记
|
||||
for(j=0;j<vec.size();j++){
|
||||
if(in[j]==0 && flag[j]==0){
|
||||
flag[j]=1;
|
||||
res.push_back(j);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
// 将该节点的下一个节点的入度减1
|
||||
for(auto a:vec[j]){
|
||||
in[a]--;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
### 拓扑排序-深度搜索
|
||||
|
||||
基于深度优先搜索的方法实现
|
||||
|
||||
深度优先搜索(或广度优先搜索)整个图,将出度为0的顶点入栈,中途要判断是否会形成环,最后输出栈得到的序列就是该图的一种拓扑排序。
|
||||
|
||||
```c++
|
||||
// 拓扑排序——深度递归+栈
|
||||
void tuopu2(vector<vector<int>> &vec,stack<int> &st,vector<int> &flag,int node){
|
||||
for(int i=0;i<vec[node].size();i++){
|
||||
if(flag[vec[node][i]]==0){
|
||||
tuopu2(vec,st,flag,vec[node][i]);
|
||||
}
|
||||
}
|
||||
if(flag[node]==0){
|
||||
st.push(node);
|
||||
flag[node]=1;
|
||||
}
|
||||
}
|
||||
```
|
||||
## 2 深度学习算子
|
||||
|
||||
|
||||
### 问题描述
|
||||
|
||||
一般深度学习网络有若干算子组成,假设一个深度学习网络模型是一个有向无环图。若算子A依赖算子B的输出,则晋档算子B执行完成后才能执行算子A,没有依赖关系的算子可以并行执行。已知每个算子的执行时间,请计算运行整个网络所需要的的最小执行时间。
|
||||
|
||||
1. 不考虑算子之间的传输时间
|
||||
2. 第一个算子为输入算子且仅有一个输入算子
|
||||
3. 算子的索引从0开始
|
||||
|
||||
输入描述
|
||||
|
||||
N+1行。N为算子的总个数,第一行输入N,N<100.第j行输入代表索引为j-2的算子的属性。包括算子的时间和算子的后继算子。
|
||||
|
||||
实例
|
||||
|
||||
```
|
||||
4
|
||||
10 1 2
|
||||
5
|
||||
1 3
|
||||
2
|
||||
```
|
||||
### 问题分析
|
||||
|
||||
1. 图问题
|
||||
2. 拓扑排序
|
||||
3. 深度搜索
|
||||
### 策略选择
|
||||
|
||||
1. 深度优先搜索
|
||||
2. 记录最长的路径
|
||||
|
||||
|
||||
### 算法设计
|
||||
|
||||
|
||||
### 算法分析
|
||||
时间复杂度 O(n*v)
|
||||
空间复杂度 O(n)
|
||||
### 算法实现
|
||||
|
||||
```c++
|
||||
// 使用深度优先搜索计算
|
||||
int dfs(vector<vector<int>> &vec,vector<int> &cost,int node,int c){
|
||||
int max_cost=c+cost[node];
|
||||
// cout<<max_cost<<endl;
|
||||
for(int i=0;i<vec[node].size();i++){
|
||||
max_cost = max(max_cost,dfs(vec,cost,vec[node][i],c+cost[node]));
|
||||
}
|
||||
return max_cost;
|
||||
}
|
||||
int main(){
|
||||
string input="4\nsoftmax 10 1 2\nrelu 5\nconv1 1 3 1\nsoftmax 2";
|
||||
istringstream cin(input);
|
||||
int N ;
|
||||
cin>>N;
|
||||
cout<<N<<endl;
|
||||
// 表示每个边花费的时间
|
||||
vector<int> cost(N,0);
|
||||
// 使用邻接链表表示关系吧。单点触发的所有边
|
||||
vector<vector<int>> vec(N,vector<int>());
|
||||
cin.ignore();
|
||||
// string input2;
|
||||
// getline(cin,input2);
|
||||
for(int i=0;i<N;i++){
|
||||
string line;
|
||||
getline(cin,line,'\n');
|
||||
// cout<<line<<endl;
|
||||
istringstream is(line);
|
||||
string name;
|
||||
is>>name;
|
||||
is>>cost[i];
|
||||
int next;
|
||||
while(is>>next){
|
||||
vec[i].push_back(next);
|
||||
}
|
||||
cout<<name<<":"<<cost[i]<<endl;
|
||||
}
|
||||
// for(auto a:vec){
|
||||
// for(auto b:a){
|
||||
// cout<<b<<" ";
|
||||
// }
|
||||
// cout<<endl;
|
||||
// }
|
||||
cout<<"result:"<<dfs(vec,cost,0,0)<<endl;
|
||||
|
||||
// 进行拓扑排序并查看结果
|
||||
|
||||
vector<int> res = tuopu1(vec);
|
||||
for(auto a:res){
|
||||
cout<<a<<" ";
|
||||
}
|
||||
cout<<endl;
|
||||
|
||||
vector<int> flag(vec.size(),0);
|
||||
stack<int> st;
|
||||
tuopu2(vec,st,flag,0);
|
||||
vector<int> res2;
|
||||
while(!st.empty()){
|
||||
cout<<st.top()<<" ";
|
||||
res2.push_back(st.top());
|
||||
st.pop();
|
||||
|
||||
}
|
||||
cout<<endl;
|
||||
// 然后根据拓扑排序计算最长时间。没哟必要
|
||||
|
||||
}
|
||||
```
|
||||
115
算法/A类:基本算法/4.4.cpp
Normal file
115
算法/A类:基本算法/4.4.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include<iostream>
|
||||
#include<vector>
|
||||
#include<algorithm>
|
||||
#include<sstream>
|
||||
#include<stack>
|
||||
using namespace std;
|
||||
// 拓扑排序——循环入度为零
|
||||
vector<int> tuopu1(vector<vector<int>> vec){
|
||||
vector<int> res;
|
||||
vector<int> in(vec.size(),0);
|
||||
vector<int> flag(vec.size(),0);
|
||||
for(int i=0;i<vec.size();i++){
|
||||
for(int j=0;j<vec[i].size();j++){
|
||||
in[vec[i][j]]++;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i=0;i<vec.size();i++){
|
||||
int j;
|
||||
// 查找入度为零的节点,添加到结果中,并标记
|
||||
for(j=0;j<vec.size();j++){
|
||||
if(in[j]==0 && flag[j]==0){
|
||||
flag[j]=1;
|
||||
res.push_back(j);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
// 将该节点的下一个节点的入度减1
|
||||
for(auto a:vec[j]){
|
||||
in[a]--;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// 拓扑排序——深度递归+栈
|
||||
void tuopu2(vector<vector<int>> &vec,stack<int> &st,vector<int> &flag,int node){
|
||||
for(int i=0;i<vec[node].size();i++){
|
||||
if(flag[vec[node][i]]==0){
|
||||
tuopu2(vec,st,flag,vec[node][i]);
|
||||
}
|
||||
}
|
||||
if(flag[node]==0){
|
||||
st.push(node);
|
||||
flag[node]=1;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用深度优先搜索计算
|
||||
int dfs(vector<vector<int>> &vec,vector<int> &cost,int node,int c){
|
||||
int max_cost=c+cost[node];
|
||||
// cout<<max_cost<<endl;
|
||||
for(int i=0;i<vec[node].size();i++){
|
||||
max_cost = max(max_cost,dfs(vec,cost,vec[node][i],c+cost[node]));
|
||||
}
|
||||
return max_cost;
|
||||
}
|
||||
int main(){
|
||||
string input="4\nsoftmax 10 1 2\nrelu 5\nconv1 1 3 1\nsoftmax 2";
|
||||
istringstream cin(input);
|
||||
int N ;
|
||||
cin>>N;
|
||||
cout<<N<<endl;
|
||||
// 表示每个边花费的时间
|
||||
vector<int> cost(N,0);
|
||||
// 使用邻接链表表示关系吧。单点触发的所有边
|
||||
vector<vector<int>> vec(N,vector<int>());
|
||||
cin.ignore();
|
||||
// string input2;
|
||||
// getline(cin,input2);
|
||||
for(int i=0;i<N;i++){
|
||||
string line;
|
||||
getline(cin,line,'\n');
|
||||
// cout<<line<<endl;
|
||||
istringstream is(line);
|
||||
string name;
|
||||
is>>name;
|
||||
is>>cost[i];
|
||||
int next;
|
||||
while(is>>next){
|
||||
vec[i].push_back(next);
|
||||
}
|
||||
cout<<name<<":"<<cost[i]<<endl;
|
||||
}
|
||||
// for(auto a:vec){
|
||||
// for(auto b:a){
|
||||
// cout<<b<<" ";
|
||||
// }
|
||||
// cout<<endl;
|
||||
// }
|
||||
cout<<"result:"<<dfs(vec,cost,0,0)<<endl;
|
||||
|
||||
// 进行拓扑排序并查看结果
|
||||
|
||||
vector<int> res = tuopu1(vec);
|
||||
for(auto a:res){
|
||||
cout<<a<<" ";
|
||||
}
|
||||
cout<<endl;
|
||||
|
||||
vector<int> flag(vec.size(),0);
|
||||
stack<int> st;
|
||||
tuopu2(vec,st,flag,0);
|
||||
vector<int> res2;
|
||||
while(!st.empty()){
|
||||
cout<<st.top()<<" ";
|
||||
res2.push_back(st.top());
|
||||
st.pop();
|
||||
|
||||
}
|
||||
cout<<endl;
|
||||
// 然后根据拓扑排序计算最长时间。没哟必要
|
||||
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
### 算法设计
|
||||
|
||||
* 归并排序」是分治思想的典型应用,它包含这样三个步骤:
|
||||
* 分解: 待排序的区间为 [l, r][l,r],令 m = \lfloor \frac{l + r}{2} \rfloorm=⌊2l+r⌋,我们把 [l, r][l,r] 分成 [l, m][l,m] 和 [m + 1, r][m+1,r]
|
||||
* 分解: 待排序的区间为 [l, r],令 $m = \lfloor \frac{l + r}{2} \rfloor$,我们把 [l, r] 分成 [l,m] 和 [m+1,r]
|
||||
* 解决: 使用归并排序递归地排序两个子序列
|
||||
* 合并: 把两个已经排好序的子序列 [l, m][l,m] 和 [m + 1, r][m+1,r] 合并起来
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度:同归并排序 O(n \log n)O(nlogn)。
|
||||
* 空间复杂度:同归并排序 O(n)O(n),
|
||||
* 时间复杂度:同归并排序 O(nlogn)。
|
||||
* 空间复杂度:同归并排序 O(n),
|
||||
|
||||
### 算法实现
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ $$
|
||||
* 树形动态规划。非线性规模增长。非线性决策过程。第二个规模增长方向构成该阶段的状态集合(每个阶段的状态并非一个),但是每个阶段的状态数量不确定。例如:三角形数组最小路径和、单向最短路径问题。时间复杂度为O(n*m)//m表示某一阶段最大的状态数量。
|
||||
* 矩阵动态规划。非线性规模增长。非线性决策。第二个规模增长方向与第一个规模增长方向独立,不受第一个规模增长影响,每个阶段的状态数量是固定的。例如背包问题、n个骰子的点数问题。O(n*m)。由于矩阵可以用来表示图。也可以定义为图型动态规划
|
||||
* 序列动态规划。非线性规模增长。非线性决策。第二个规模增长的方向不受第一个规模增长的影响,矩阵动态规划的特殊形式,针对序列的动态规划。例如:最长公共子序列、正则表达式匹配。O(n*m)
|
||||
* 二维规模增长的冬天规划,第一维表示的行,第二维表示列。第一维为主要的规模增长方向,第二维是第一维某一阶段的状态集合。
|
||||
* 二维规模增长的动态规划,第一维表示的行,第二维表示列。第一维为主要的规模增长方向,第二维是第一维某一阶段的状态集合。
|
||||
## 4.1 线性动态规划
|
||||
|
||||
## 4.2 树型动态规划
|
||||
|
||||
@@ -47,7 +47,7 @@ $$
|
||||
### 算法实现
|
||||
|
||||
```
|
||||
//@五分钟学算法//www.cxyxiaowu.compublic int lengthOfLIS(int[] nums) {
|
||||
int lengthOfLIS(int[] nums) {
|
||||
if (nums == null || nums.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ $$
|
||||
|
||||
### 算法实现
|
||||
```
|
||||
//@五分钟学算法//www.cxyxiaowu.compublic int minCost(int[][] costs) {
|
||||
int minCost(int[][] costs) {
|
||||
if (costs == null || costs.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,13 +28,13 @@ F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
|
||||
### 选择策略
|
||||
* 数组
|
||||
* 动态规划
|
||||
* 原理: 以斐波那契数列性质 f(n + 1) = f(n) + f(n - 1)f(n+1)=f(n)+f(n−1) 为转移方程。
|
||||
* 原理: 以斐波那契数列性质f(n+1)=f(n)+f(n−1) 为转移方程。
|
||||
|
||||
### 算法设计
|
||||
|
||||
* 状态定义: 设$dp$为一维数组,其中 $dp[i]$ 的值代表 斐波那契数列第 $i$个数字 。
|
||||
* 转移方程: $dp[i + 1] = dp[i] + dp[i - 1]$,即对应数列定义 $f(n + 1) = f(n) + f(n - 1)$;
|
||||
* 初始状态: $dp[0] = 0 dp[1] = 1$,即初始化前两个数字;
|
||||
* 初始状态: $dp[0] = 0,dp[1] = 1$,即初始化前两个数字;
|
||||
* 返回值:$dp[n]$ ,即斐波那契数列的第 n 个数字。
|
||||
|
||||
|
||||
@@ -122,12 +122,12 @@ public int climbStairs(int n) {
|
||||
### 算法设计
|
||||
|
||||
* **状态定义**: 设动态规划列表 dp ,dp[i]dp[i] 代表以元素 nums[i]nums[i] 为结尾的连续子数组最大和。
|
||||
* 为何定义最大和 dp[i]dp[i] 中必须包含元素 nums[i]nums[i] :保证 dp[i]dp[i] 递推到 dp[i+1]dp[i+1] 的正确性;如果不包含 nums[i]nums[i] ,递推时则不满足题目的 连续子数组 要求。
|
||||
* **转移方程**: 若 dp[i-1] \leq 0dp[i−1]≤0 ,说明 dp[i - 1]dp[i−1] 对 dp[i]dp[i] 产生负贡献,即 dp[i-1] + nums[i]dp[i−1]+nums[i] 还不如 nums[i]nums[i] 本身大。
|
||||
* 当 dp[i - 1] > 0dp[i−1]>0 时:执行 dp[i] = dp[i-1] + nums[i]dp[i]=dp[i−1]+nums[i]
|
||||
* 当 dp[i - 1] \leq 0dp[i−1]≤0 时:执行 dp[i] = nums[i]dp[i]=nums[i] ;
|
||||
* 为何定义最大和 dp[i] 中必须包含元素 nums[i] :保证dp[i] 递推到 dp[i+1]的正确性;如果不包含nums[i] ,递推时则不满足题目的 连续子数组 要求。
|
||||
* **转移方程**: 若dp[i−1]≤0 ,说明 dp[i - 1]对 dp[i] 产生负贡献,即 dp[i-1] + nums[i]还不如 nums[i]本身大。
|
||||
* 当 dp[i - 1] > 0时:执行 dp[i] = dp[i-1] + nums[i]
|
||||
* 当 dp[i−1]≤0 时:执行 dp[i] = nums[i];
|
||||
* **初始状态**: dp[0] = nums[0]dp[0]=nums[0],即以 nums[0]nums[0] 结尾的连续子数组最大和为 nums[0]nums[0] 。
|
||||
* **返回值**: 返回 dpdp 列表中的最大值,代表全局最大值。
|
||||
* **返回值**: 返回 dp 列表中的最大值,代表全局最大值。
|
||||
|
||||

|
||||
|
||||
|
||||
83
算法/A类:基本算法/5.10 零钱兑换.md
Normal file
83
算法/A类:基本算法/5.10 零钱兑换.md
Normal file
@@ -0,0 +1,83 @@
|
||||
## 零钱兑换
|
||||
|
||||
### 问题描述
|
||||
|
||||
给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
|
||||
|
||||
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。
|
||||
|
||||
假设每一种面额的硬币有无限个。
|
||||
|
||||
```
|
||||
输入:amount = 5, coins = [1, 2, 5]
|
||||
输出:4
|
||||
解释:有四种方式可以凑成总金额:
|
||||
5=5
|
||||
5=2+2+1
|
||||
5=2+1+1+1
|
||||
5=1+1+1+1+1
|
||||
```
|
||||
|
||||
### 问题分析
|
||||
|
||||
* 典型的动态规划
|
||||
* 典型的完全背包问题
|
||||
|
||||
### 策略选择
|
||||
* 动态规划
|
||||
* 采取两个方向上的动态规划。两个方向上的动态规划是非等价独立的。
|
||||
* 数组矩阵线性数据结构
|
||||
|
||||
|
||||
### 算法设计
|
||||
|
||||
* 阶段划分:规模增长的方向有两个。第一个规模增长的方向是硬币的种类。第二个规模增长的方向是金钱的总量。
|
||||
* 状态变量dp[i][j]表示使用第i个硬币,达到j分钱所有的可能。
|
||||
* 状态转移方程
|
||||
* if k*coins[i]== amount dp[i][j]++;
|
||||
* if k*coins[i]<amount dp[i][j]+=dp[i-1][j-k*conis[i]] foreach k
|
||||
* 边界条件
|
||||
* 当amount=0应该只有一种策略,一个不选。当amount=n、i=coins.size()终止
|
||||
|
||||
* 补充:可以对状态空间进行压缩,提出了一种改进方案。直接以硬币的数目作为第二层遍历。
|
||||
### 算法分析
|
||||
|
||||
时间复杂度O(m*n)
|
||||
空间复杂度O(m*n)
|
||||
|
||||
### 算法实现
|
||||
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
public:
|
||||
// 其实就是个很简单的完全背包问题。完全可以设置两个规模增长方向
|
||||
int change2(int amount, vector<int>& coins) {
|
||||
vector<int> dp(amount + 1);
|
||||
dp[0] = 1;
|
||||
for (int& coin : coins) {
|
||||
for (int i = coin; i <= amount; i++) {
|
||||
dp[i] += dp[i - coin];
|
||||
}
|
||||
}
|
||||
return dp[amount];
|
||||
}
|
||||
// 使用两个规模增长方向
|
||||
int change(int amount, vector<int>& coins) {
|
||||
if(amount==0)return 1;
|
||||
vector<vector<int>> dp(coins.size()+1,vector<int>(amount + 1,0));
|
||||
for (int k=1;k<=coins.size();k++) {
|
||||
int coin=coins[k-1];
|
||||
for(int i=1;i<=amount;i++){
|
||||
for(int j=0;j*coin<=i;j++){
|
||||
if(j*coin==i){
|
||||
dp[k][i]++;
|
||||
}
|
||||
dp[k][i]+=dp[k-1][i-j*coin];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[coins.size()][amount];
|
||||
}
|
||||
};
|
||||
```
|
||||
@@ -72,4 +72,44 @@ public:
|
||||
return dp[m - 1][n - 1];
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## 递归法
|
||||
|
||||
|
||||
|
||||
### 代码实现
|
||||
```
|
||||
class Solution {
|
||||
public:
|
||||
bool isMatch(string s, string p) {
|
||||
return isMatchR(s,p,0,0);
|
||||
}
|
||||
// 递归和循环混搭,果然很恶心。直接使用递归好了。
|
||||
bool isMatchR(string &s,string &p,int i,int j){
|
||||
//递归终止的条件
|
||||
if(i>=s.size() && j>=p.size()){
|
||||
return true;
|
||||
}
|
||||
// cout<<i<<j<<endl;
|
||||
// *判断。
|
||||
if(j+1<p.size() && p[j+1]=='*'){
|
||||
// 匹配零次 || 匹配一次
|
||||
return isMatchR(s,p,i,j+2) || ((i<s.size()) && (p[j]==s[i]||p[j]=='.')&&isMatchR(s,p,i+1,j));
|
||||
|
||||
}
|
||||
// 如果下一个不是*
|
||||
if(i<s.size() && j<p.size() && p[j]==s[i]){
|
||||
return isMatchR(s,p,i+1,j+1);
|
||||
}
|
||||
if(i<s.size() && j<p.size() && p[j]=='.'){
|
||||
return isMatchR(s,p,i+1,j+1);
|
||||
}
|
||||
// 不匹配
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
```
|
||||
Reference in New Issue
Block a user