序言:

经过长达十几天的集训,确实学了不少知识点。我想如果再不总结的话,6天之后又要忘完了。

所以发一篇具有总结回忆性的博客,供大家回忆。

目录会本人自己排列的时间的先后顺序来排列,可直接食用。

目录:

一 、 动态规划1.区间DP二 、 STL1.vector2.pair3.set4.map5.priority_queue
三 、数据结构1.字符串hash2.hash表3.树状数组4.堆及其运用
四 、 图论最短路1.Floyd2.Dijkst3.SPFA4.Bellman-Ford及其优化优先队列 O(mlogn)fionacci堆O(nlogn + m)这个本人能力实在有限便不说了,想知道的可以去问问

动态规划

动态规划总共分为三个部分:
- 状态和状态变量

- 阶段和阶段变量- 策略和最优策略- 状态转移方程

什么是区间动态规划?

区间DP也是分为这三个部分,虽然它也是属于线性Dp的一种,但是它的阶段是以“区间长度”为单位。

区间DP的初始形态一般就由长度为111的“元区间”所构成的。它需要用左端点、右端点描述每个维度,在一些特殊的题中有可能会用到中点。

第一题 石子合并:

合并石子1

若最初的的第lll堆石子和rrr堆石子被合并在一起,则说明lrl~rl r之间的每堆石子也已经被合并,这样lll和rrr才有可能相邻。

因此,在任何时刻,任意一堆石子均可以用一个闭区间[l,r]来描述,表示这一堆是由最初的第lrl~rl r堆石子合并成的。

所以假设 如果有3堆石子,
则有2种合并方案,((1,2),3)和(1,(2,3))
如果有k堆石子呢?

不管怎么合并,总之最后总会归结为2堆,如果我们把最后两堆分开,左边和右边无论怎么合并,都必须满足最优合并方案,整个问题才能得到最优解。

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;
int min(int x,int y){return x>y?y:x;
}
int dp[105][105];
int s[105];
int n,x,k;
int main(){scanf("%d",&n);memset(dp,0x3f3f3f3f,sizeof(dp));for(int i=1;i<=n;i++){scanf("%d",&x);s[i]=s[i-1]+x;}for(int i=1;i<=n;i++){dp[i][i]=0;}for(int len=2;len<=n;len++){for(int j=1;j<=n-len+1;j++){int r=j+len-1;for(int k=1;k<r;k++)dp[j][r]=min(dp[j][r],dp[j][k]+dp[k+1][r]);dp[j][r]+=(s[r]-s[j-1]);}}printf("%d",dp[1][n]);return 0;
}

第二题能量项链:

能量项链

这道题几乎只是与合并石子有点区别,此时它是一个环,序列的长度是合并石子的两倍。

只需要循环的长度变成两倍即可。

思路就是:

用 dp[i][j]dp[i][j]dp[i][j] 表示合并区间 iii 到 jjj 的最大能量,

第一重循环表示珠子分组的终点,第二重循环的表示从珠子分组的起点 ,第三重循环表示截断的点。

AC代码:

#include<iostream>
using namespace std;
int n,e[205],f[205][205]={0};
int main()
{int i,j,k,mx=0;cin>>n;for(i=1;i<=n;i++){cin>>e[i];e[n+i]=e[i];}for(j=2;j<=2*n-1;j++)for(i=j-1;i>0&&j-i<n;i--)for(k=i;k<j;k++)f[i][j]=max(f[i][k]+f[k+1][j]+e[i]*e[k+1]*e[j+1],f[i][j]);for(i=1;i<=n;i++)mx=max(mx,f[i][i+n-1]);cout<<mx; return 0;
}

前面的都是版题以及版题的变式

现在来一点不是板子题的题。

第三题戳西瓜

戳西瓜

从题来看,我第一次是没看懂是说的一个什么意思。

不过有各位大佬,鼎力相助我才读懂这道题,并进而AC这道题

首先分析一下如果我戳破了第iii个,那么会留下i−1i-1i−1和i+1i+1i+1,但是此时你的和却跟i−1i-1i−1,iii,i+1i+1i+1都有关。

从这个条件不难看出它是一个(i-1,i+1)的开区间。

但是题目却要我们戳完所有的气球。怎么办呢?

这是我们就可以,设两个端点最左端点和最右端点分别为:

a[0]a[0]a[0]和a[n+1]a[n+1]a[n+1]且初值都赋值为111。

现在就不存在存在i−1i - 1i−1和i+1i + 1i+1是开区间的情况了。

设状态dp[i][j]dp[i][j]dp[i][j],表示戳破第i个到第j个的西瓜。

目标为dp[0][n+1]dp[0][n + 1]dp[0][n+1]。

气球 iii 和气球 jjj 之间的所有气球都可能是最后被戳破的那一个,

假设最后戳破的为 kkk

因为最后戳的是kkk所以要先把iii到kkk的全部戳破,答案为dp[i][k]dp[i][k]dp[i][k]

然后kkk到jjj肯定也要戳完,答案为dp[k][j]dp[k][j]dp[k][j]

现在在气球iii到气球jjj之间就只剩下iii,kkk,jjj了,

所以戳爆kkk的价值就是nums[i]∗nums[k]∗nums[j]nums[i]*nums[k]*nums[j]nums[i]∗nums[k]∗nums[j]

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>using namespace std;
int dp[505][505];
int a[505];
int main(){int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);}n++;a[0]=1;a[n+1]=1;for (int i = n; i >= 0; i--){for (int j = i + 1; j < n + 2; j++){for (int k = i + 1; k < j; k++) {dp[i][j]=max(dp[i][j], dp[i][k] + dp[k][j] + a[i]*a[j]*a[k]);}}}printf("%d",dp[0][n+1]);return 0;
}

二·二叉堆

在这之前我们需要一滴滴的知识储备就是,树。

完全二叉树:

如果一棵深度为k的二叉树,1至k-1层都是满的,即每层结点数满足2i-1,只有最下面一层的结点数小于2i-1,并且最下面一层的结点都集中在该层最左边的若干位置,则此二叉树称为完全二叉树。

那么我们所学的二叉堆呢,总的来说就是一个数组。

它可以被看作一棵完全二叉树。树中每个结点与数组中存放该结

点中值的那个元素相对应。如图所示::

设数组AAA的长度为lenlenlen,二叉树的结点个数为sizesizesize,

size≤lenize≤lenize≤len,则A[i]A[i]A[i]存储二叉树中编号为i的结点值

(111≤iii≤sizesizesize),而A[size]A[size]A[size]以后的元素并不属于相应的堆,

树的根为A[1]A[1]A[1],并且利用完全二叉树的性质,

我们很容易求第iii个结点的父结点fa(i)fa(i)fa(i)、左孩子lch(i)lch(i)lch(i)、

右孩子rch(i)rch(i)rch(i)的下标,分别是i/2i/2i/2、2i2i2i、2i+12i+12i+1。

除了这一性质之外,同时对除根以外的每个结点i,A[fa(i)]≥A[i]。

即除根结点以外,所有结点的值都不得超过其父结点的值,

这样就推出,堆中最大元素存放在根结点中,

且每一结点的子树中的结点值都小于等于该结点的值,

这种二叉堆又称为“大根堆”;反之,称为“小根堆”。

堆一般有两个重要的操作,putputput(往堆中加入一个元素)和

getgetget(从堆中取出并删除一个元素),putputput操作(也可用于建堆,

首先创建一个小根堆为例):

1、在堆尾加入一个元素,并把这个结点置为当前结点。2、比较当前结点和它父结点的大小
如果当前结点小于父结点,则交换它们的值,并把父结点置为当前结点,
继续转2。
如果当前结点大于等于父结点,则转3。3、结束。

以此循环nnn次,即可建立一个小根堆出来。

具体的操作请见其他各大博客,均有提及。

那么现在来看一下建小根堆的代码:

#include <cstdio>
#include <algorithm>using namespace std;
const int maxn = 15;
int heap[maxn], n, heap_size[maxn];
void PUT(int k){int fa, now;heap[++heap_size] = k;//把现在需要插入的数放到堆尾 now  = heap_size;//把堆尾的这个数设为当前操作数 while(now > 1){//向上与父节点比较知道比完了各节点结束 fa = now >> 1;if(heap[now] >= heap[fa])//不小于父节点的值就结束 break;swap(heap[now], heap[fa]);//交换函数 now  = fa;//交换后又继续往上比较 }
}int main(){scanf("%d", &n);for(int i = 1;i <= n;i++){//插入输入 scanf("%d",&a);PUT(a);}for(int i = 1;i <= n;i ++){//输出小根堆 printf("%d", heap[i]);}return 0;
}

从堆中去除并删除的getgetget操作:
1、取出堆中根结点的值。

2、把堆的最后一个结点(heapsizeheap_sizeheaps​ize)放到根的位置上,把根覆盖掉,堆长度减一。

3、把根结点置为当前父结点,即当前操作结点nownownow。

4、如果nownownow无儿子(now>heapsize/2now>heap_size/2now>heaps​ize/2),则转6;否则,把nownownow的两(或一)个儿子中值较小的那一个置为当前子结点sonsonson。

5、比较nownownow与sonsonson的值,如果nownownow的值小于等于sonsonson,转6;否则交换两个结点的值,把nownownow指向sonsonson,转4。

6、结束。

由于技术问题,本人找不到这张流程图,在这里道歉。

那么就来看一下删除根节点操作的代码吧:

int Get(){int now, son ,res;res  = heap[1];//保存根节点 heap[1] = heap[heap_size --]// 堆尾节点替换根节点 now = 1;//把根节点置为当前节点开始向下与儿子比较 while(now * 2 <= heap_size){//当前节点非叶子节点就继续 son = now * 2;//初始化左儿子 if(son < heap_size && heap[son + 1]< heap[son])son ++;//有右儿子,且右儿子比左儿子削,son指向右儿子 if(heap[now] <= heap[son])//如果你爸没你儿子大,结束 break;swap(heap[now], heap[son]);//不然就交换 now  = son;//把你儿子设为你现在的老子,继续向下比 }return res;//取出原堆中的根节点
}

例题一堆排序:

堆排序
一道简单的小根堆,只需要用swapswapswap交换更小的数,最后用一重

nnn次的循环依次取出并删除根结点就行了,即执行getgetget操作即可。

//正常的小根堆
//加上void swap()一个swap
//把那个数放到根节点上
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>using namespace std;
int heap_size,n;
int heap[10000005];
void swap(int &a,int &b){int t=a;a=b;b=t;
}
void put(int d){int now,fa;heap[++heap_size]=d;now=heap_size;while(now>1){fa=now>>1;if(heap[now]>=heap[fa]) return ;swap(heap[now],heap[fa]);now=fa;}
}
int Get(){int now,fa,res;res=heap[1];heap[1]=heap[heap_size--];now=1;while(now*2<=heap_size){fa=now*2;if(fa<heap_size && heap[fa+1]<heap[fa]) fa++;if(heap[now]<=heap[fa]) return res;swap(heap[now],heap[fa]);now=fa;}return res;
}
void work(){int x,y,ans=0;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&x);put(x);}for(int i=1;i<=n;i++){printf("%d ",Get());}
}
int main(){work();return 0;
}

例题二合并果子:

合并果子
这道题与模板没有区别,首先通过nnn次操作putputput建立一个小根堆

进而,不断重复提出两个数并删除,再加起来形成一个新节点再

进行同样的putputput操作,三个为一组,共有n−1n - 1n−1组,最后的答案

是ansansans。

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;int heap_size, heap[10005];
int n;
void swap(int &x, int &y){int t = x, x = y, y = t;
}void put(int x){int son, fa;heap[++heap_size] = x;son = heap_size;while(son > 1){fa =  son >> 1;if(heap[son] >= heap[fa]) break;swap(heap[son] ,heap[fa]);son =  fa;}
}void get(){int fa, son, res;res = heap[1];heap[1] = heap[heap_size --];fa = 1;while(fa * 2 <= heap_size){son = fa *2;if(son < heap_size && heap[son + 1] < heap[son]) son ++;if(heap[fa] <= heap[son]) break;swap(heap[fa], heap[son]);fa = son;}return res;
}int main(){int x, y, ans = 0;scanf("%d",&n);for(int i = 1;i <= n;i++){sacnf("%d",&x);put(x);}for(int i = 1;i <= n;i++){x = get();y = get();ans += x + y;put(x + y);}printf("%d\n",ans);return 0;
}

例题三小猫钓鱼:

小猫钓鱼

本题一看就是某位同学最喜欢做的思维贪心题。

首先可以这么假设,如果知道了取到最大值得情况,当人最后在

第iii个池塘里钓鱼,那么时间至少是固定的,所以尽可能选取

钓到的鱼最多的池塘。所以,暴力枚举nnn次鱼塘的位置,即可。

那么有贪心,必定会有动态规划了,假设opt[t][n]opt[t][n]opt[t][n]表示第ttt分钟

人在第iii个鱼塘里钓到的鱼数。则:

opt[t][n] = maxinum(opt[t - k][n - 1] + s);

暴力枚举kkk,sss为t−k+1t - k + 1t−k+1到ttt之间,除开从第n−1n - 1n−1的鱼塘走到

第nnn个鱼塘的时间,在第nnn个鱼塘中可以钓到的鱼数。

好吧,进入正题。

建立以fishfishfish为键值的大根堆,包括能钓到鱼的数量和池塘的编号

然后借助枚举创造条件。即可得出正解。

#include <iostream>
#include <cstdio>
using namespace std;
struct Data {int fish, lake;  //堆中结点的信息
};
Data heap[105];
int t[105], f[105], d[105];
int Max, k, t1;
void maintain(int i){  //维护堆Data a;int next;a = heap[i];next = i * 2;while (next <= k) {if (next < k && heap[next].fish < heap[next + 1].fish)next++;if (a.fish < heap[next].fish) {heap[i] = heap[next];i = next;next *= 2;} elsebreak;}heap[i] = a;
}
void work() {int i, j, m, n;cin >> n;for (i = 1; i <= n; i++) cin >> f[i];for (i = 1; i <= n; i++) cin >> d[i];for (i = 1; i < n; i++) cin >> t[i];cin >> m;for (k = 1; k <= n; k++) {int Time = m - t1;int ans = 0;for (i = 1; i <= k; i++) {heap[i].fish = f[i];heap[i].lake = i;}for (i = 1; i <= k / 2; i++) maintain(i);while (Time > 0 && heap[1].fish > 0) {ans += heap[1].fish;heap[1].fish -= d[heap[1].lake];maintain(1);Time--;}if (ans > Max)Max = ans;t1 += t[k];}cout << Max << endl;
}int main() {work();return 0;
}

这个知识点也是需要将每个函数的具体框架,记牢记熟。

和后面的STLSTLSTL相呼应。


三·STL

WarningWarningWarning:

本章节,请还未食用过栈和队列的无关战斗人员尽快离开战斗现场

强行食用,会引来强烈不适,谨慎食用。

2.vector

什么是vectorvectorvector?

vector直译为“向量”,一般说成“变长数组”,也就是长度根据需要而自动改变的数组.

有什么用?

有些题目需要开很多数组,往往造成内存超限,使用vectorvectorvector简单方便,还可节省空间。

定义:

vector<typename> name;//<变量类型> 变量名

以上定义相当于定义了一个一维数组name[size]name[size]name[size],只是sizesizesize不确

定,其长度可以根据需要而变化。其中,typenametypenametypename可以是任何基

本类型,如intintint、doubledoubledouble、charcharchar、结构体等,也可以是容器

vector<int> a;                 //定义了一个整型不定长数组a
vector<double> score;   //定义了一个双精度浮点型不定长数组score
vector<node> stu;          //定义了一个结构体类型的不定长数组stu

注意:
如果typenametypenametypename也是一个STLSTLSTL容器,那么定义时需要在两个“>”符号之间加一个空格,不然会被误认为是位运算的右移符号“<<”

forexamplefor exampleforexample:

vector<vector<int>  > a;  //定义了一个两个维度都可变的二维整型数组a vector<int> a[100];           //定义了一个第一维长度为100,第二位不限长度的二维数组a

关于vectorvectorvector的访问:
访问vectorvectorvector中的元素一般有两种方式:下标访问 和 迭代器

(iteratoriteratoriterator)访问。

第一种是通过“下标”访问的。

例如,对于容器vector<int>vvector<int> vvector<int>v,可以使用v[index]来访问

它的第indexindexindex个元素。其中,0≤index≤v.size()–10≤index≤v.size() – 10≤index≤v.size()–1,

v.size()v.size()v.size()表示vectorvectorvector中元素的个数。

第二种是通过“迭代器”访问的。

可以将迭代器理解为一种类似指针的变量,使用前需要提前定义,

其定义为:vector<typename>::iteratoritvector<typename>::iterator itvector<typename>::iteratorit,这个ititit就

是一个迭代器,可以通过∗it*it∗it来访问该容器里的元素值

例如:

 vector<int>::iterator it = v.begin();   //定义一个迭代器it,初始化为容器v的首元素地址,这是*it相当//于v[0],*(it + i)相当于v[i]。

迭代器还可以进行自加自减操作,如it++it++it++,++it++it++it,it−−it--it−−,−−it--it−−it,

注意:迭代器不支持it<v.end()it<v.end()it<v.end()的写法,

只能是it!=v.end()it != v.end()it!=v.end(), v.end()v.end()v.end()并不是取v容器尾元素地址,

而是尾元素下一个地址。

例如:

for(vector<int>::iterator it = v.begin(); it != v.end(); it ++)
printf(“%d”,*it);

常用函数:

(1) push_back()
解释:push_back(x)将x添加到容器最后,时间复杂度为O(1)。

(2) size()
解释:如果是一维数组,size()用来获得vector中元素个数;如果是二维数组,size()用来获得vector中第二维的元素个数,时间复杂度为O(1),同时,还可以使用resize(n)重设数组大小。
例如输出12300:

vector<int> v;
for(int i = 1; i <= 3; i ++) v.push_back(i);
v.resize(5);
for(int i = 0; i < v.size(); i ++) printf(“%d”,v[i]);

(3) pop_back()
解释:用来删除vector中的尾元素。时间复杂度为O(1),例如以下代码输出12:

 vector<int> v;for(int i = 1; i <= 3; i ++) v.push_back(i);v.pop_back();for(int i = 0; i < v.size(); i ++) printf(“%d”,v[i]);

(4) clear()
解释:用来清空vector中的所有元素。时间复杂度为O(n),例如以下代码输出0:

  vector<int> v;for(int i = 1; i <= 3; i ++) v.push_back(i);v.clear();printf(“%d”,v.size());
  1. insert()
    解释:insert(it, x)用来向vector任意迭代器it处插入元素x。时间复杂度为O(n),例如以下代码输出1 2 -1 3 4 5:
#include <cstdio>
#include <vector>using namespace std;
int main(){vector<int> v;//注意打空格养成好习惯 for(int i = 1;i <= 5;i ++)v.push_back(i);vector<int> :: iterator it = v.begin();v.insert(it + 2, -1);for(; it != v.end();it ++)printf("%d ",*it);return 0;
}

(6) erase()
解释:erase()erase()erase()用来删除vectorvectorvector中的元素,有两种用法,

一是erase(it)erase(it)erase(it),删除迭代器ititit处的单个元素.

二是erase(first,last)erase(first, last)erase(first,last),删除左闭右开区间[first,last][first, last][first,last]内的所有元素。例如:

#include <cstdio>
#include <vector>using namespace std;
int main(){vector<int> v;for(int i = 1;i <= 5;i++){v.push_back(i);}vector<int> :: irerator it = v.begin();v.erase(it + 1);v.erase(it + 2,i + 4);for(int i = 0;i < v.size();i++)printf("%d ",v[i]);return 0;
}

第一题上网统计:

上网统计

一道版题,只需要处理字符,如果遇到同一个人则合并。

由于用户名和浏览的网页名长度不固定,

用vectorvectorvector解决比较方便,可以分别用“下标”访问和“迭代器”访问。

//下标访问
#include<cstdio>
#include<cstdio>
#include<vector>using namespace std;
class std::vector<int> a;
int main(){for(int i=1;i<=3;i++) a.push_back(i);a.resize(5);for(int i=1;i<=a.size();i++){printf("%d",a[i]);}return 0;
}
#include<bits/std++>using namespace std;
vector<int> a;
int main(){for(int i=1;i<=3;i++) a.push_back(i);for(vector<int>::itrator it=a.begin();it!=a.end();it++){printf("%d",*it);}return 0;
}
#include<cstdio>
#include<vector>using namespace std;
int main(){vector<int> v;for(int i=1;i<=5;i++) v.push_back(i);vector<int>::iterator it=v.begin();v.insert(it+2,-1);for(;it!=v.end();it++)printf("%d ",*it);return 0;
}
//迭代器访问
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<bits/stdc++.h>using namespace std;
struct node{string ID;vector<string> WEB;
};
int main(){string id,web;vector<node> v;int n,m;cin>>n>>m;for(int i=1;i<=m;i++){cin>>id>>web;bool f=0;for(int j=0;j<v.size();j++){if(v[j].ID == id){v[j].WEB.push_back(web);f=1;break;}}if(f==0){node _new;_new.ID=id;_new.WEB.clear();_new.WEB.push_back(web);v.push_back(_new);}}for(int i=0;i<v.size();i++){cout<< v[i].ID;for(int j=0;j<v[i].WEB.size();j++){cout<<" "<<v[i].WEB[j];}cout<<endl;}return 0;
}

2.优先队列priority_queue:

优先队列优先级如何设置:

大根堆优先队列的定义:

priorty_queue<int> q;            //默认为大顶堆优先队列
priorty_queue<int,vector<int>,less<int>  > q;//好习惯

小根堆优先队列的定义:

priorty_queue<int,vector<int>,greater<int>  > q;

例如以下代码输出1:

priorty_queue<int,vector<int>,greater<int>  > q;
q.push(3);
q.push(4);
q.push(1);
printf(“%d\n”,q.top());

第二题有序表的最小和

有序表的最小和

如果用枚举出所有和,把它们依次压入小根堆优先队列,取出n个堆顶元素即可,但n的范围是400000,n方的算法的时间复杂度可想而知,直接舍去,但是由于两个序列已经是从小到大排序了的,我们来分析一下下表的数据:
第一行    A[1]+B[1] ≤ A[1]+B[2] ≤ A[1]+B[3] ≤ ······
第二行    A[2]+B[1] ≤ A[2]+B[2] ≤ A[2]+B[3] ≤ ······
······
第n行     A[n]+B[1] ≤ A[n]+B[2] ≤ A[n]+B[3] ≤ ······

显然每一行的第一项和都是这一行最小的和,所以不妨把每一行

的第一项和先压进优先队列里,再取出堆顶元素)并输出,

如果取的是第i行的元素,就把第i行的下一个元素压入,让堆中

始终保持n个元素和。

AC代码:

#include<bits/stdc++.h>using namespace std;
int const maxn=4e5+5;
struct num{int id;int value;bool operator <(const num x) const {return  value>x.value ;}
};
int a[maxn],b[maxn],c[maxn];
priority_queue<num> q;
int main() {int n;scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);for(int i=1;i<=n;i++) scanf("%d",&b[i]);for(int i=1;i<=n;i++){c[i]=2;num tmp;tmp.value=a[i]+b[1];tmp.id=i;q.push(tmp);}for(int i=1;i<=n;i++){num tmp=q.top();q.pop();printf("%d\n",tmp.value);tmp.value=a[tmp.id]+b[c[tmp.id]++];q.push(tmp);}return 0;
}

3.映射map:

什么是mapmapmap?

map翻译为映射,其实数组就是一种映射。比如$int a[100];$,就是定义了一个int到int的映射,而$a[5]=25;$是把5映射到25,他们是一一对应的,数组总是把$int$类型映射到其它基本类型,因为数组下标只能是$int$。但有时希望把$string$映射成一个$int$数组就不方便了,这时就可以使用$map$,它可以将任何基本类型(包括容器)映射到任何基本类型。

mapmapmap常用的三种情形:

1.需要建立字符(串)与整数之间的映射,使用map可以减少代码量;2.判断大整数(比如几千位)或者其它类型数据是否存在,可以把
map当bool类型数组使用(哈希表);3.字符串与字符串之间的映射。

定义:

map<typename1, typename2> name;

其中,typename1是映射前的类型(键key),typename2是映射后

的类型(值value),name为映射的名字,例如:

map<int, int> mp;//普通int数组
map<string, int> mp;//字符串到整型的映射
map<set<int>, string> mp;//键和值也可以是容器

mapmapmap的键对值必须唯一,

也就是说键keykeykey必须唯一,值valuevaluevalue不一定。

mapmapmap的访问:

关于mapmapmap的访问,也是分为下标和迭代器两种
下标访问(例):

map<char, int> mp;
用mp[‘c’]mp[‘c’]mp[‘c’] 来访问它对应的元素,如mp[‘c’]=124mp[‘c’] = 124mp[‘c’]=124.

迭代器访问(例):

map<typename1, typename2>::iterator it;
因为mapmapmap的每一对映射都有两个typenametypenametypename,所以使用“it−>firstit -> firstit−>first”来访问键,而使用“it−>secondit -> secondit−>second”来访问值。例如:

map<char, int> mp;
mp[‘m’] = 20;     mp[‘r’] = 30;     mp[‘a’] = 40;
for(map<char, int>::iterator it = mp.begin(); it != mp.end(); it ++)printf(“%c %d\n”,it -> first, it -> second);

输出答案:

a 40
m 20
r 30

map在建立映射的同时,会自动实现按照键从小到大排序。

因为map内部使用“红黑树”实现,后面set也是。

那什么是红黑树?

红黑树(RedBlackTreeRed Black TreeRedBlackTree) 是一种自平衡二叉查找树,

是在计算机科学中用到的一种数据结构,典型的用途是实现关联

数组。红黑树是一种特定类型的二叉树,它是在计算机科学中用

来组织数据比如数字的块的一种结构。若一棵二叉查找树是红黑

树,则它的任一子树必为红黑树。


map的常用函数:

(1)find() 和 size()

解释:find(key)是返回键为key的映射的迭代器,
时间复杂度为O(log2n),n为map中映射的对数。
size()用来获得map中映射的对数,时间复杂度为O(1)。

例如以下代码输出3 b 30:

map<char, int> mp;
mp[‘a’] = 20;
mp[‘b’] = 30;
mp[‘c’] = 40;
printf(“%d ”, mp.size());
map<char, int>::iterator it = mp.find(‘b’);
printf(“%c %d\n”,it -> first, it -> second);

(2)erase()

删除一个区间内所有元素用erase(first, last)。其中,
first为区间的起始迭代器;
last为区间的末尾迭代器的下一个地址,
也就是左闭右开区间[first, last) 。
时间复杂度为O(first - last)。

下面代码输出aaa 202020:

map<char, int> mp;
mp[‘a’] = 20;
mp[‘b’] = 30;
mp[‘c’] = 40;
map<char, int>::iterator it = mp.find(‘b’);
mp.erase(it, mp.end());
for(map<char, int>::iterator it = mp.begin(); it != mp.end(); it ++)printf(“%c %d\n”,it -> first, it -> second);

(3)clear()

clear()用来清空map。时间复杂度为O(n)。

vectorvectorvector的用法一样

map<char, int> mp;
mp.clear();

第三题查字典:

查字典
这是一道经典的map映射题,这是一道很有趣的题,

由于GMGMGM通过单词来查询页码,即单词是键,页码是值,

查询时输入键,即可在O(1)时间内查询值,也就是对应页码。

这里只需要用到mapmapmap基本函数即可

AC代码:

#include<bits/stdc++.h>using namespace std;
map<string,int> dic;
int main(){int n,m;int page;scanf("%d",&n);for(int i=1;i<=n;i++){string s;cin>>s;scanf("%d",&page);dic[s]=page;}scanf("%d",&m);while(m--){string s; cin>>s;printf("%d\n",dic[s]);}return 0;
}

二元结构体pair

什么是pairpairpair?

pairpairpair是”二元结构体”的替代品,将两个元素捆绑在一起,

节省编码时间。如同定义一个这个:

struct pair {typename1 first;typename2 second;
};

如何定义?
要使用pairpairpair,必须先添加头文件,即#include<utility>,同时需要using namespace std;。因为map的内部实现涉及pairpairpair,因此添加mapmapmap头文件时会自动添加utilityutilityutility头文件,此时可以省去utilityutilityutility头文件。

pair<typename1, typename2> name;

如何初始化?

例如:定义一个参数为string和int类型的pair,并同时初始化

在这里我就讲那个最好记的那个:

pair<string, int> p(“haha”, 5);

注意
pairpairpair可以直接做比较运算,比较的规则是先以firstfirstfirst的大小作为

标准,只有当first相等时才去判断secondsecondsecond的大小。

forexample:for example:forexample:
以下一段代码输出p1 < p3 p1 <= p3 p1 < p2

pair<int, int> p1(5, 10);
pair<int, int> p2(5, 15);
pair<int, int> p3(10, 5);
if(p1 < p3) printf(“p1<p3”);
if(p1 <= p3) printf(“p1<=p3”);
if(p1 < p2) printf(“p1<p2”);

由于mapmapmap可以根据键值自动排序,而pairpairpair又可以比较大小,

所以pairpairpair可插入到同类型的map中并根据it->first排序,

(注意,如果it->first相同则根据mapmapmap键值唯一的特性,只保留先输入的二元组)

例如输出:

leimu 20
rem 51
#include <cstdio>
#include <map>
#include <iostream>using namespace std;
int main(){map<string, int> mp;mp.insert(make_pair("leimu",20));mp.insert(pair<string, int>("rem", 51));for(map<string, int>iterator it = mp.begin(); it != mp.end();it ++)cout << it -> first <<" "<< it -> second << endl;return 0;
}

第四题Let the Balloon

Let the Balloon Rise

这道题其实我现在都是懵懂的,所以我只能献上GMGMGM所赞助的

PPTPPTPPT的思路了:

AC代码:

#include<bits/stdc++.h>using namespace std;
int main(){int n;while(scanf("%d",&n),n){map<string,int> ma;map<string,int>::iterator x;map<string,int>::iterator y;for(int i=0;i<n;i++){string mm;cin>>mm;x=ma.find(mm);if(x==ma.end()){ma.insert(make_pair(mm,1));}else{ma[mm]++;}}int MAX=0;for(x=ma.begin(); x!= ma.end();x++){if(x -> second>=MAX){MAX=x -> second;y=x;}}cout<<y -> first<<endl;}return 0;
}

set集合

好了最后一个了。

什么是setsetset?

setsetset翻译为集合,是一个内部自动有序且不含重复元素的容器。

setsetset最主要的作用就是自动去重并按升序排序,

因此遇到需要去重但是又不方便直接开数组的情况,

比如元素比较多或者类型不是intintint,可以尝试用setsetset解决。

setsetset中的元素是唯一的,内部同样采用“红黑树”实现。

如何定义:

set<typename> name;

其中,typenametypenametypename可以是任何基本类型或者容器,namenamename是集合的名字,例:

set<int> st;

当然也可以定义set数组,例:

set<int> st[100];

这样st[0]st[0]st[0]~st[99]st[99]st[99]中的每一个元素都是一个setsetset容器。

setsetset的访问方式同上。

setsetset只能通过迭代器访问,即先定义一个迭代器:

set<typename>::iterator it;

注意:
然后使用∗it*it∗it来访问set中的元素。setsetset也不支持(it +i )it < st.end()的访问方式,实际上除了vectorvectorvector和stringstringstring之外的STL容器都不支持。

例如:

set<int> st;
st.insert(3);
st.insert(5);
st.insert(2);
st.insert(3);
for(set<int>::iterator it = st.begin(); it != st.end(); it ++)printf(“%d”, *it);

setsetset常用函数:
(1)insert()insert()insert()和 size()size()size()

解释:insert(x)用来将x插入到set中,并自动递增排序和去重,
时间复杂度为  O(log2n),n为set中元素的个数。
size()用来获得set中的元素个数,时间复杂度为O(1)。

(2)find()find()find()

解释:find(value)是返回set中对应值value的迭代器
(可以把it看成地址,*it看成地址对应的值),
时间复杂度为O(log2n)。

例如以下一段代码输出“3 2”。

set<int> st;
for(int i = 1; i <= 3; i ++) st.insert(i);
printf(“%d”, st.size());
printf(“%d”, *(st.find(2)));

(3)clear()clear()clear()

解释:clear()用来清空set中的所有元素,时间复杂度为O(n)。

(4)erase()

解释:erase()可以删除单个元素,也可以删除一个区间内的所有元素。
删除单个元素可以用erase(it),其中it为要删除元素的迭代器,
时间复杂度为O(1)。也可以用erase(value),value为要删除元素的值,
时间复杂度为O(log2n)。

下面代码输出“1 3 200 ”:

set<int> st;
for(int i = 1; i <= 3; i ++) st.insert(i);
st.erase(st.find(2));
for(set<int>::iterator it = st.begin(); it != st.end(); it ++) printf(“%d ”, *it);
st.clear();
st.insert(100);
st.insert(200);
st.erase(100);
for(set<int>::iterator it = st.begin(); it != st.end(); it ++)
printf(“%d ”, *it);

eraseeraseerase还有一个神奇的用法,

删除一个区间内所有元素用erase(first, last)。
其中,first为区间的起始迭代器;last为区间的末尾迭代器的下一个地
址,也就是左闭右开区间[first, last) 。
时间复杂度为O(first - last)。

下面代码输出“1 2 3 4 5 6 7 8 9 ”:

set<int> st;
for(int i = 1; i <= 100; i ++) st.insert(i);
set<int>::iterator it = st.find(10);
st.erase(it, st.end());
for(it = st.begin(); it != st.end(); it ++)printf(“%d ”, *it);

例题题海战:

因为本人实力有限,只有五十分代码。

等哪天我ACACAC再贴上来。


感谢GM的友情赞助,因为本章节多数是拿来背的,比如这些概念以及重载运算符,所以概念这些,都是借鉴的PPT上的。但是代码是自己亲手码的,勿喷。


四、HASH

前言:对于这个版块,我是真的懵逼,GM交了我们这么化解

HASH冲突的方法,结果LFLFLF一来说,其实HASH主要是

字符串HASH,而且也没有这么多需要化解的冲突的题。

(理解是不可能理解的,这辈子都不可能理解的。机房里个个都是

人才,说话也好听,我超喜欢里面的。)

所以至于化解冲突的方法,我就不贴上来了。

如有需要,请点击

那我来讲一下构造HASH函数的方法:

1.直接定址法
2.除后余数法
3.平方取中法
4.数字分析法
5.折叠法
6. 随机数法

第一种,好处在于以关键码 key 的某个线性函数值为哈希地址

,不会产生冲突。

·····

·····

·····

·····(以上几种省略,不知道该怎么写,也没有什么优化)

第六种,写随机数,(写对拍),这是一个随缘的样例但是

但是却有针对性,如果在短时间想不出来比较复杂的样例

这个就是一个明智之举。


在实际操作中需视不同情况采用不同的哈希函数。

通常考虑的因素:

   (1)计算哈希函数所需时间(包括硬件指令的因素);(2)关键字的长度;(3)哈希表的大小;(4)关键字的分布情况;(5)记录的查找频率。

说的概念是真的蒙,因为是优化程序的一种途径

所以说将一些例题吧。

第一题疯狂的搜索

#include <cstdio>
#include <algorithm>
#include <bits/stdc++.h>using namespace std;
bool hash[16000005];
char a[16000005];
int n,nc;
int id[305];
int main(){while(~scanf("%d %d",&n,&nc)){//取反输出memset(hash,0,sizeof(hash));memset(id,-1,sizeof(id));scanf("%s",a);int cnt=0;int len=strlen(a);for(int i=1;i<=len && cnt<nc;i++){if(id[a[i]]!=-1) continue;id[a[i]]=cnt++;}int ans=0;for(int i=0;i<len-n+1;i++){int sum=0;for(int j=i;j<n+i;j++)sum=sum*nc+id[a[j]];//切换进制if(hash[sum]) continue;ans++;hash[sum]=1; }printf("%d\n" ,ans);}
}

第二题兔子与兔子

这道题主要是求出字符串 strstrstr 的所有的前缀字符串的HASH值,那么,strstrstr的所有

子串的HASH值都可以在 O(1)的时间内计算出来

然后就是求子串区间 [L, R]:

pre[R] - pre[L - 1] * p^ (R - L + 1)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;const int p = 131;
const int Maxn = 1000015;
int n;
int query, l1, r1, l2, r2;
unsigned long long pre[Maxn];   //各个前缀 的哈希值
unsigned long long L, R;
unsigned long long p_pow[Maxn];
char str[Maxn];int main(){scanf("%s", str + 1);n = strlen(str + 1);pre[0] = 0;for(int i = 1; i <= n; i++){pre[i] = pre[i - 1] * p + str[i] - 'a' + 1; }p_pow[0] = 1;for(int i = 1; i < Maxn; i++){p_pow[i] = p_pow[i - 1] * p;}scanf("%d", &query);for(int q = 1; q <= query; q++){scanf("%d %d %d %d", &l1, &r1, &l2, &r2);   L = pre[r1] - pre[l1 - 1] * p_pow[r1 - l1 + 1];R = pre[r2] - pre[l2 - 1] * p_pow[r2 - l2 + 1];if(L == R){printf("Yes\n");}else{printf("No\n");}}return 0;
}

五·树状数组

前言:
这章节的代码框架,真就是一个字

什么是树状数组(又是这个问题哈)

树状数组(Binary Indexed Tree(B.I.T)也称作Fenwick Tree)是一个区间查询和单点

修改复杂度都为log(n)的数据结构。主要用于查询任意两点之间的所有元素之和。

说白了,就是个数组,只是长得像树。

questionquestionquestion:

有一个一维数组长度为n,求区间[L,R]的和,并且可以对原数组某一元素进行修改?

现在就引入LowbitLowbitLowbit的概念了。

LowbitLowbitLowbit:

lowbit(i)lowbit(i)lowbit(i)的意思是将 iii 转化成二进制数之后,只保留最低位的1及其后面的0,

截断前面的内容,然后再转成十进制数,这个数也是树状数组中i号位的子叶数。

forexamplefor exampleforexample:

lowbit(22)lowbit(22)lowbit(22)的意思是将 22 转化成二进制数之后,得到10110,保留末位的1及其

后的0,并截断前面的内容,得到10,转化为十进制数为2,即lowbit(22)lowbit(22)lowbit(22)=2,

证明C[22]的子叶数为2个。

那么LowbitLowbitLowbit有什么用呢?

废话那不构造树状数组C[i]的吗

好了,写一下吧:

#include<cstdio>
int A[10]={0,1,2,3,4,5,6,7,8},C[10];
int lowbit(int x){return x & -x;
}int main(){for(int i = 1; i <= 8; i ++)for(int j = i - lowbit(i) + 1; j <= i; j ++)C[i] += A[j];for(int i = 1; i <= 8; i ++)printf("%d ",C[i]);return 0;
}

那张图我就不拿出来了,实在拿不出来。

这玩意还可以对原数组A[i]A[i]A[i]进行更新(update)操作

 void update(int k,int x)// A[k]+x 操作 {for(int i = k; i <= n; i += lowbit(i))C[i] += x;}

说到 UpdateUpdateUpdate 那我顺便就说一下对树状数组C[i]进行初始化操作

#include<cstdio>
int A[10], C[10];
int lowbit( int  x ){    //求lowbitreturn x  &  -x;
}void update(int  k , int  x){ //更新C[i]for(int i = k; i <= 8; i += lowbit(i))C[i] += x;
}int main(){for(int i = 1; i <= 8; i ++){  //输入时预处理,构造C[i]scanf("%d", &A[i]);update( i, A[i]);}for(int i = 1; i <= 8; i ++) //输出C[i]printf("%d ", C[i]);return 0;
}

除此之外,还第三个作用就是求前缀和(Sum)

解释一下 : 因为这个区间就是前缀和(1 ~ i)所以求是求前缀和。

 int Sum(int k){for(int i = k; i > 0; i -= lowbit(i) )B[k] += C[i];return  B[k];      }

延伸出来,就是可以求前缀和B[i]B[i]B[i]了

#include<cstdio>int A[10],B[10],C[10];int lowbit(int x){return x & -x;}void update(int k,int x){for(int i = k; i <= 8; i += lowbit(i))C[i] += x;}void Sum(int k){for(int i = k; i > 0; i -= lowbit(i))B[k] += C[i];    }int main(){for(int i = 1; i <= 8; i ++){scanf("%d", &A[i]);update(i , A[i]);Sum(i);}for(int i = 1; i <= 8; i ++)printf("%d ",B[i]);return 0;}

好了,我讲完了现在就来砍几道题吧。

第一题单点修改、区间查询

#include <bits/stdc++.h>using namespace std;
const int MAXN = 1e6 + 5;
int n, m;
long long BIT[MAXN];
int a[MAXN];
int lowbit(int x) { return x & (-x); }void Update(int x, int y) {for (int i = x; i <= n; i += lowbit(i)) BIT[i] += y;
}long long Sum(int x) {long long ans = 0;for (int i = x; i; i -= lowbit(i)) ans += BIT[i];return ans;
}int main() {scanf("%d %d", &n, &m);for (int i = 1; i <= n; i++) {scanf("%d", &a[i]);Update(i, a[i]);}for (int i = 1; i <= m; i++) {int p, l, r;scanf("%d %d %d", &p, &l, &r);if (p == 1) {Update(l, r);} else {printf("%lld\n", Sum(r) - Sum(l - 1));}}return 0;
}

第二题区间修改、单点查询

#include <bits/stdc++.h>
const int maxn = 1e6 + 5;using namespace std;
long long BIT[maxn];
int a[maxn], b[maxn];
int n, m;
int lowbit(int x){return x & (-x);
} void Update(int x,int y){for(int i = x;i <= n;i += lowbit(i))BIT[i] += y;
}long long Sum(int x){long long ans = 0;for(int i = x;i > 0;i -= lowbit(i))ans += BIT[i];return ans;
}int main(){scanf("%d %d", &n, &m);for(int i = 1;i <= n;i++){scanf("%d", &a[i]);b[i]=a[i] - a[i - 1];}for(int i = 1;i <= n;i++){Update(i,b[i]);}for(int i = 1;i <= m;i++){int p;int l,r,k;scanf("%d", &p);if(p == 1){scanf("%d %d %d", &l, &r, &k);Update(l , k);Update(r + 1 , -k);}else if(p == 2){int k;scanf("%d", &k);printf("%lld\n", Sum(k));}}return 0;
}

第三题区间修改、区间查询

#include<cstdio>
#include<cstring>
#include<algorithm>
#define lowbit(a) ((a)&(-(a)))
#define ll long long
using namespace std;
const int M=1e6+10;
int n,m,x;
ll bit1[M],bit2[M];
void add(int a,ll b){ll t=a;for(;a<=n;a+=lowbit(a)){bit1[a]+=b;bit2[a]+=b*t;}
}
ll query(int a){ll t1=0,t2=0,ls=a;for(;a;a-=lowbit(a)){t1+=bit1[a];t2+=bit2[a];}t1*=(ls+1);return t1-t2;
}
ll range_query(int l,int r){return query(r)-query(l-1);
}
void range_add(int l,int r,ll v){add(l,v);add(r+1,-v);
}
int l,r,opt;
int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&x);range_add(i,i,x);}while(m--){scanf("%d%d%d",&opt,&l,&r);if(opt==1){scanf("%d",&x);range_add(l,r,x);}else{printf("%lld\n",range_query(l,r));}}return 0;
}

那么二维的这几道题只是会有一点点改变其余,没改变。只是思路会变一点。


二维树状数组 3:区间修改,区间查询

#include<bits/stdc++.h>using namespace std;
const int maxn=2100;
long long BIT1[maxn][maxn],BIT2[maxn][maxn],BIT3[maxn][maxn],BIT4[maxn][maxn];
int n,m;
int lowbit(int x){return x&(-x);
}void updata(int x,int y,long long z){for(int i = x;i <= n;i += lowbit(i)){for(int j = y;j <= n;j += lowbit(j)){BIT1[i][j] += z;BIT2[i][j] += x*z;BIT3[i][j] += y*z;BIT4[i][j] += x*y*z;}}
}
long long Sum(int x,int y){long long ans = 0;for(int i = x;i > 0;i -= lowbit(i)){for(int j = y;j > 0;j -= lowbit(j)){ans += (x + 1) * (y + 1) * BIT1[i][j] - (y + 1) * BIT2[i][j]-(x + 1) * BIT3[i][j] + BIT4[i][j];}}return ans;
}
int main()
{while(scanf("%d %d",&n,&m) == 2){memset(BIT1, 0, sizeof(BIT1));memset(BIT2, 0, sizeof(BIT2));memset(BIT3, 0, sizeof(BIT3));memset(BIT4, 0, sizeof(BIT4));int p;while(scanf("%d",&p) == 1){if(p == 1){int a,b,c,d;long long x;scanf("%d %d %d %d %lld",&a,&b,&c,&d,&x);updata(a,b,x);updata(c+1,b,-x);updata(a,d+1,-x);updata(c+1,d+1,x);}else{int x1,y1,x2,y2;scanf("%d %d %d %d",&x1,&y1,&x2,&y2);printf("%lld\n",Sum(x2,y2) - Sum(x1-1,y2) - Sum(x2,y1-1) + Sum(x1-1,y1-1));}}}return 0;
}

暑假集训总结——区间DP,堆的概念及应用,STL(vector、set、pair、map、priority_queue),hash表,树状数组,图论相关推荐

  1. 树形DP+树状数组 HDU 5877 Weak Pair

    1 //树形DP+树状数组 HDU 5877 Weak Pair 2 // 思路:用树状数组每次加k/a[i],每个节点ans+=Sum(a[i]) 表示每次加大于等于a[i]的值 3 // 这道题要 ...

  2. SPOJ D-query 树状数组离线 求区间内不同数字的个数

    Given a sequence of n numbers a1, a2, -, an and a number of d-queries. A d-query is a pair (i, j) (1 ...

  3. HDU-5542-The Battle of Chibi【树状数组+dp】

    HDU-5542-The Battle of Chibi[树状数组+dp] Time Limit: 6000/4000 MS (Java/Others) Memory Limit: 65535/655 ...

  4. 51nod 1680区间求和 (dp+树状数组/线段树)

    不妨考虑已知一个区间[l,r]的k=1.k=2....k=r-l+1这些数的答案ans(只是这一个区间,不包含子区间) 那么如果加入一个新的数字a[i](i = r+1) 则新区间[l, i]的答案为 ...

  5. 2023牛客寒假算法基础集训营4_20230130「向上取整」「夹逼dp」「lowbit科学+树状数组性质」「搜索」「倍增跳表」「莫队」

    6/13 教育场是有被教育到.(预计会鸽几题. 已过非太水的题们 //B //https://ac.nowcoder.com/acm/contest/46812/B//小构造小数学#include & ...

  6. Educational Codeforces Round 80 (Rated for Div. 2)SZU cf集训round2 C~E(dp,状压+二分,树状数组+逆向思维)

    C. Two Arrays 题目大意:就是给定两个整数n和m.计算数组对的数量(a,b),使得: 1 .两个阵列的长度都等于m: 2 .每个数组的每个元素都是1到n(包括1和n)之间的整数: 从1到m ...

  7. acwing 297. 赤壁之战 树状数组优化DP 寒假集训

    题目链接 想要求长度为M的子序列,我们可以拿DP方程来计算,并且这个DP也是比较好看出来的DP[i][j]代表着i后j位置中的所有长度为j的子序列,递推方程为 for(int i=1;i<=n; ...

  8. NEFU大一暑假集训-树状数组

    题集链接 目录: OP A Ultra-QuickSort 题目大意 思路 代码 B Stars 题目大意 思路 代码 C Mobile phones 题目大意 思路 代码 D Cows 题目大意 思 ...

  9. bzoj 1264: [AHOI2006]基因匹配Match (树状数组优化dp)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1264 思路: n大小为20000*5,而一般的dp求最长公共子序列复杂度是 n*n的,所以我 ...

最新文章

  1. ( KMP 求循环节的个数)Power Strings -- poj -- 2406
  2. 在android手机上运行PHP
  3. VC++ MFC中如何将应用程序的配置信息保存到注册表中(一)
  4. 2017-06-09 问题
  5. mfc 弹簧_弹簧和线程:事务
  6. Android BitmapShader 实战 实现圆形、圆角图片
  7. 动态HTML处理和机器图像识别
  8. Nature封面引发持续热议,死亡猪脑恢复部分功能可实现永生?官方答疑来了
  9. windows安装php和mysql
  10. Object-C中的非正式协议与正式协议
  11. 人工神经网络可以做什么,人工神经网络有什么用
  12. 等保三级密码技术应用要求 GM/T 0054-2018
  13. win10 1050ti anaconda搭建tensorflow-gpu
  14. 使用Adobe illustrator (AI)快速制作图标
  15. 无缘中兴(拒绝了offer)
  16. 每周一品 · 音圈电机(VCM)中的磁性材料
  17. 克转换成千克怎么算python_如何在Python中将磅转换为千克
  18. 基于AndroidStudio员工绩效考核评价系统app设计
  19. 一维数组的创建及使用
  20. 触角云开发的微信商城系统

热门文章

  1. Google天气和股票API
  2. 搭建SpringBoot+Vue 项目 完整流程
  3. SQL语句中生成UUID方法
  4. CSSHTMLREM制作手机端网页(小练习)
  5. 从知识女性转变为家庭妇女
  6. 分布式和非分布式_分布式防御虚假信息
  7. Spring获取上下文的四种方式方式
  8. WPF作图神器Interactive DataDisplay的初步使用
  9. 实习html周日志,实习日志:最后的一周
  10. 警告: Request method ‘POST‘ not supported。的原因之一——空格毁一生