第4章 贪心算法

贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。

贪心算法的基本要素

1、贪心选择性质
所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。
2、最优子结构性质
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。


例1.活动安排问题:在所给的活动集合中选出最大的相容活动子集合
测试样例
input:
4
1 2
1 3
2 4
4 5
output:
3
input:
5
1 3
2 4
3 6
3 5
5 6
output:
3

分析:
由于输入的活动以其完成时间的非减序排列,所以算法每次总是选择具有最早完成时间的相容活动加入集合A中。直观上,按这种方法选择相容活动为未安排活动留下尽可能多的时间。也就是说,该算法的贪心选择的意义是使剩余的可安排时间段极大化,以便安排尽可能多的相容活动。
当输入的活动已按结束时间的非减序排列,算法只需O(n)的时间安排n个活动,使最多的活动能相容地使用公共资源。如果所给出的活动未按非减序排列,可以用O(nlogn)的时间重排。

代码:

#include<iostream>
#include<stdlib.h>
#include<math.h>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct seg
{int s,e;
} s[10010];
int cmp(seg x,seg y)
{return x.e<y.e;
}
int main()
{int i,n,num;while(~scanf("%d",&n)){num=1;for(i=0; i<n; i++){scanf("%d%d",&s[i].s,&s[i].e);}sort(s,s+n,cmp);int time=s[0].e;for(i=1; i<n; i++){if(time<=s[i].s){time=s[i].e;num++;}}printf("%d\n",num);}return 0;
}

例2.活动安排问题的拓展:有若干个活动,第i个开始时间和结束时间是[Si,fi),活动之间不能交叠,要把活动都安排完,至少需要几个教室?
思路:求活动叠加的最大值
分析:将时间标记为1个时间结点(flag标记其为开始还是结束时间),然后将所有时间结点按时间大小排序,初始化交叠次数为0,接下来开始遍历时间结点,若为开始时间,则交叠次数+1,若为结束时间,则交叠次数-1,记录交叠次数的最大值,即为所求。
测试样例
input:
5
1 3
2 4
3 5
3 6
5 6
output:
3
对测试样例的处理过程
(1)n个活动,给所有时间标志结束还是开始,形成2*n个时间结点
1 0
3 1
2 0
4 1
3 0
5 1
3 0
6 1
5 0
6 1
(2)给时间结点排序
1 0
2 0
3 1 //时间值为3,标记为结束时间的排在前
3 0 //时间值为3,标记为结束时间的排在后
3 0
4 1
5 1
5 0
6 1
6 1

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
struct seg //活动
{int s;int e;
}a[10010];
int cmp1(seg x,seg y) //先将活动按结束时间排序
{return x.e<y.e;
}
struct node //时间结点,flag标记:0代表开始时间,1代表结束时间
{int vlue;int flag;
}b[20010];
int cmp2(node x,node y) //时间结点排序
{return x.vlue<y.vlue;
}
int main()
{int n,sum;while(cin>>n){sum=0;for(int i=0; i<n; i++){cin>>a[i].s>>a[i].e;}sort(a,a+n,cmp1);  //先对活动按先结束时间排序,这样就可以使得下面的时间结点排序中,int k=0;           //两个活动:一个的开始时间与另一个的结束时间相同时,先将结束时间放前面for(int i=0; i<n; i++){b[k].vlue=a[i].s; b[k++].flag=0;//0代表开始时间b[k].vlue=a[i].e; b[k++].flag=1;//1代表结束时间}sort(b,b+k,cmp2); //给时间结点排序,由于上面活动的排序,时间结点排序时,会将值相同的两个时间结点,结束标记的先排在前面int Max=0;        //以便于下面记录次数交叠时,先对该时间结束的活动记录交叠次数-1,再对该时间开始的活动记录交叠次数+1for(int i=0; i<k; i++){if(b[i].flag==0) //活动开始{sum++; //交叠次数+1if(Max<sum){Max=sum;}}else{sum--; //活动结束,交叠次数-1}}cout<<Max<<endl;}
}

例3.最优装载(背包)问题
给定n种物品和一个背包。物品i的重量是Wi,其价值为Vi,背包的容量为C。在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部装入背包,应如何选择装入背包的物品,使得装入背包中物品的总价值最大?

用贪心算法解背包问题的基本步骤:
1.首先计算每种物品单位重量的价值Vi/Wi;
2.然后依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包;
3.若将这些物品全部装入背包后,背包内的物品总重量未超过C,则选择单位重量价值次高的物品并尽可能多地装入背包
4.依此策略一直地进行下去,直到背包装满为止。

代码:

//C++#include<iostream>
#include<stdlib.h>
#include<math.h>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct item
{double weight;double value;
} a[1010];
int cmp(item x,item y)
{return (x.value/x.weight)>(y.value/y.weight);
}
int main()
{int i,n;double v;while(~scanf("%d%lf",&n,&v)){double sum=0;for(i=0; i<n; i++){scanf("%lf%lf",&a[i].weight,&a[i].value);}sort(a,a+n,cmp);for(i=0; i<n; i++){if(a[i].weight<v){v-=a[i].weight;sum+=a[i].value;}else break;}if(v>0&&i!=n){sum+=(a[i].value/a[i].weight)*v;}printf("%lf\n",sum);}return 0;
}//C
/*
#include<stdio.h>
#include<math.h>
int wi[1010],val[1010];
void Sort(int wi[],int val[],int n)
{for(int i=0; i<n-1; i++){for(int j=0; j<n-i-1; j++){double t1=val[j]*1.0/wi[j];double t2=val[j+1]*1.0/wi[j+1];if(t1<t2){int temp1,temp2;temp1=wi[j];wi[j]=wi[j+1];wi[j+1]=temp1;temp2=val[j];val[j]=val[j+1];val[j+1]=temp2;}}}
}
int main()
{int i,n;double v;while(~scanf("%d%lf",&n,&v)){double sum=0;for(i=0; i<n; i++){scanf("%d%d",&wi[i],&val[i]);}Sort(wi,val,n);for(i=0; i<n; i++){if(wi[i]<v){v-=wi[i];sum+=val[i];}else break;}if(v>0&&i!=n){sum+=(val[i]*1.0/wi[i])*v;}printf("%lf\n",sum);}return 0;
}
*/

例4.哈夫曼编码
哈夫曼编码算法用字符在文件中出现的频率表来建立一个用0,1串表示各字符的最优表示方式。
给出现频率高的字符较短的编码,出现频率较低的字符以较长的编码,可以大大缩短总码长。
前缀码:对每一个字符规定一个0,1串作为其代码,并要求任一字符的代码都不是其它字符代码的前缀。这种编码称为前缀码。

代码:

//哈夫曼编码简单实现(对应切割木棒问题)
#include<iostream>
#include<stdlib.h>
#include<math.h>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
struct cmp
{bool operator() (int &a,int &b){return a>b;}
};
int main()
{priority_queue<int, vector<int>, cmp>que;int i,n,x,sum;while(cin>>n){sum=0;for(i=0; i<n; i++){cin>>x;que.push(x);}while(que.size()!=1){int a=que.top();que.pop();int b=que.top();que.pop();int c=a+b;que.push(c);sum+=c;cout<<a<<" + "<<b<<" = "<<c<<endl;}while(!que.empty()){que.pop();}cout<<sum<<endl;}return 0;
}

例5.最小生成树问题
设G =(V,E)是无向连通带权图,即一个网络。E中每条边(v,w)的权为c[v][w]。如果G的子图G’是一棵包含G的所有顶点的树,则称G’为G的生成树。生成树上各边权的总和称为该生成树的耗费。在G的所有生成树中,耗费最小的生成树称为G的最小生成树。

最小生成树性质
设G=(V,E)是连通带权图,U是V的真子集。如果(u,v)属于E,且u属于U,v属于V-U,且在所有这样的边中,(u,v)的权c[u][v]最小,那么一定存在G的一棵最小生成树,它以(u,v)为其中一条边。这个性质有时也称为MST性质。

1、Prim算法
设G=(V,E)是连通带权图,V={1,2,…,n}。

构造G的最小生成树的Prim算法的基本思想是:
首先置S={1},然后,只要S是V的真子集,就作如下的贪心选择:
选取满足条件i属于S,j属于V-S,且c[i][j]最小的边,将顶点j添加到S中。这个过程一直进行到S=V时为止。
在这个过程中选取到的所有边恰好构成G的一棵最小生成树。

按Prim算法顺序得到的最小生成树上的边如下图所示:

时间复杂度:O(n2)

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
#define Max 0x3f3f3f3f
int ch[1005][1005],low[1005],vis[1005]; //分别记录边权、当前最小边权、标记访问
int n;
int prim()
{int pos=1,sum=0;memset(vis,0,sizeof(vis));vis[pos]=1;  //先将1加入集合 for(int i=2; i<=n; i++){low[i]=ch[pos][i];}for(int i=1; i<n; i++)  //将n-1个顶点加入集合 {int Min=Max;for(int j=1; j<=n; j++) //找出不在集合中且边最小的顶点 {if(!vis[j]&&Min>low[j]){Min=low[j];pos=j;}}sum+=Min;vis[pos]=1;for(int j=1; j<=n; j++)  //随着新顶点的加入而更新其他顶点的最小边权 {if(!vis[j]&&low[j]>ch[pos][j]){low[j]=ch[pos][j];}}}return sum;
}
int main()
{int m,s,e,w;while(cin>>n>>m){memset(ch,Max,sizeof(ch));for(int i=0; i<m; i++){cin>>s>>e>>w;ch[s][e]=ch[e][s]=w;}int ans=prim();cout<<ans<<endl;}return 0;
}

2、Kruskal算法(避环法)
Kruskal算法构造G的最小生成树的基本思想是:
(1)首先将G的n个顶点看成n个孤立的连通分支。
(2)将所有的边按权从小到大排序。
(3)然后从第一条边开始,依边权递增的顺序查看每一条边
(4)按下述方法连接2个不同的连通分支:
当查看到第k条边(v,w)时,如果端点v和w分别是当前2个不同的连通分支T1和T2中的顶点时,就用边(v,w)将T1和T2连接成一个连通分支,然后继续查看第k+1条边;
如果端点v和w在当前的同一个连通分支中,就直接再查看第k+1条边。这个过程一直进行到只剩下一个连通分支时为止。

按Kruskal算法顺序得到的最小生成树上的边如下图所示:

算法复杂度:
当图的边数为e时,Kruskal算法所需的计算时间是O(eloge)。当时,Kruskal算法比Prim算法差,但当时,Kruskal算法却比Prim算法好得多。(即边少时Kruskal算法优于Prim)

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int f[1010];
int find(int x)
{return f[x]==x?x:f[x]=find(f[x]);
}
struct city
{int a;int b;int c;
}ch[6000];
int cmp(city x,city y)
{return x.c<y.c;
}
int main()
{int i,n,m;while(cin>>n>>m){int sum=0;for(i=1; i<=n; i++){f[i]=i;}for(i=1; i<=m; i++){cin>>ch[i].a>>ch[i].b>>ch[i].c;}sort(ch+1,ch+1+m,cmp);for(i=1; i<=m; i++){int xx=find(ch[i].a);int yy=find(ch[i].b);if(xx!=yy){if(xx>yy){int temp;temp=xx;xx=yy;yy=temp;}f[yy]=xx;sum+=ch[i].c;}}cout<<sum<<endl;}return 0;
}

例6.最短路径问题

1.Dijkstra(迪杰斯特拉)算法

典型的最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。

基本思想:设置顶点集合S并不断地作贪心选择来扩充这个集合。初始时,S中仅含有源。设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组dis记录当前每个顶点所对应的最短路径长度。Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dis作必要的修改。一旦S包含了所有V中顶点,dis就记录了从源到所有其它顶点之间的最短路径长度。

算法复杂度:
时效性较好,时间复杂度为O(V2+E)
源点可达的话,O(V*lgV+E*lgV)=> O(E*lgV)
当是稀疏图的情况时,此时E=V2/lgV,所以算法的时间复杂度可为O(V2
若是斐波那契堆作优先队列的话,算法时间复杂度,则为O(V*lgV + E)

代码:

/*
无法判断是否存在负权边,非负边权图中效率稳定
使用范围:求单源(单源点的最短路径问题是指:给定一个加权有向图G和源点s,对于图G中的任意一点v,求从s到v的最短路径。)、无负权的最短路。权值非负时使用
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define INF 0x3ffffff
using namespace std;
int ch[505][505];
int dis[505];
int vis[505];
//int path[505];
int main()
{int n,m,i,j,x,y,l,source,des;while(cin >> n >>m){//cin>>source>>des;for(i=0; i<n; i++){for(j=0; j<n; j++){ch[i][j] = INF;}}for(i=0; i<n; i++){dis[i] = INF;vis[i] = 0;//path[i]=source;}//dis[source]=0;dis[0] = 0;for(i=0; i<m; i++){scanf("%d%d%d",&x,&y,&l);ch[x][y] = min(ch[x][y],l);ch[y][x] = min(ch[y][x],l);}for(i=0; i<n; i++) //遍历n个顶点{int Min = INF;int s=-1;for(j=0; j<n; j++) //每次找一个最小权顶点 {if(Min > dis[j] && !vis[j]){s = j;Min = dis[j];}}if(s!=-1){vis[s] = 1;for(j=0; j<n; j++){dis[j] = min(dis[s] + ch[s][j],dis[j]);/* if(dis[j]>dis[s]+ch[s][j]){dis[j]=dis[s]+ch[s][j];path[j]=s;}*/}}}for(i=0; i<n; i++){for(j=0; j<n; j++){printf("%d ",ch[i][j]);}printf("\n");}for(j=0; j<n; j++){printf("%d ",dis[j]);}cout << endl;/*for(i=0; i<n; i++)//求具体路径{if(i==s) continue;printf("从%d到%d最短路为 :%d\t",s,i,dis[i]);int next;next=path[i];printf("%d--",i);while(next!=path[next]){printf("%d--",next);next=path[next];}printf("%d\n",s);}*/return 0;
}

2.Bellman-Ford算法

求解单源最短路径问题的一种算法。求单源最短路,可以判断有无负权回路(若有,则不存在最短路),与Dijkstra算法不同的是,在Bellman-Ford算法中,边的权值可以为负数。

Bellman-Ford算法的流程如下:
给定图G(V, E)(其中V、E分别为图G的顶点集与边集),源点s,
数组dis[i]记录从源点s到顶点i的路径长度,初始化数组dis[n]为MAX,dis[s]为0;
以下操作循环执行至多n-1次,n为顶点数:
对于每一条边e(u, v),如果dis[u] + w(u, v) < dis[v],则令dis[v] = dis[u]+w(u, v)。w(u, v)为边e(u,v)的权值;
若上述操作没有对dis进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;
为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在dis[u] + w(u, v) < dis[v]的边,则图中存在负环路,即是该图无法求出单源最短路径。否则数组dis[n]中记录的就是源点s到各顶点的最短路径长度。

时间复杂度:O(V*E)。

代码:

/*
求含负权图的单源最短路径算法,可检测并输出负圈效率较低
使用范围:当权值有负值,且可能存在负圈时使用
*/
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#define MAX 200000000    //表示无穷远
using namespace std;
struct node              //结构体存边
{int x,y;int l;
} a[10005];
int n,m;                 //n个点,m条边
int dis[105];            //到每个点的最短距离
int main()
{int i,j,x,y,l;while(~scanf("%d%d",&n,&m)&&(n||m)){for(i=1; i<=n; i++){dis[i]=MAX;}j=0;for(i=0; i<m; i++){scanf("%d%d%d",&x,&y,&l);a[j].x = x;a[j].y = y;a[j++].l = l;a[j].x = y;a[j].y = x;a[j++].l = l;}dis[1]=0;                      //起点标记为0bool flag = 1;for(i=0; i<n-1; i++){if(flag){flag=0;for(j=0; j<2*m; j++){if(dis[a[j].y] > dis[a[j].x]+a[j].l){dis[a[j].y] = dis[a[j].x]+a[j].l;flag = 1;}}}elsebreak;}/* flag = 0;                                       //检验是否存在负环for(i=0; i<2*m; i++){if(dis[a[i].y] > dis[a[i].x]+a[i].l)          //如果if句成立 表明上循环完成后再循环中出现负环{dis[a[i].y] = dis[[i].x]+a[i].l;flag = 1;}}*/printf("%d\n",dis[n]);}return 0;
}

3.SPFA算法
Bellman-Ford的队列优化,与Bellman-ford算法类似,SPFA算法采用一系列的松弛操作以得到从某一个节点出发到达图中其它所有节点的最短路径。不同的是,SPFA算法通过维护一个队列,使得一个节点的当前最短路径被更新之后没有必要立刻去更新其他的节点,从而大大减少了重复的操作次数。

与Dijkstra算法与Bellman-ford算法都不同,SPFA的算法时间效率是不稳定的,即它对于不同的图所需要的时间有很大的差别。
SPFA算法在负边权图上可以完全取代Bellman-ford算法,另外在稀疏图中也表现良好。但是在非负边权图中,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法,以及它的使用堆优化的版本。

时间复杂度:O(kE)(k<<V)。
在最好情形下,每一个节点都只入队一次,则算法实际上变为广度优先遍历,其时间复杂度仅为O(E)。如果每一个节点都被入队(V-1)次,算法退化为Bellman-ford算法,其时间复杂度为O(VE)。

代码:

/*
Bellman的队列优化  可检测但不能输出负圈
使用范围:当权值有负值且没有负圈时使用SPFA
*/
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <queue>
#include <vector>
#define MAX 20000000
using namespace std;
struct node
{int x;int l;
};
vector<node> a[105];     //邻接表存图
queue<int> q;
int n,m;
int vis[105];            //标记是否在队列中
int dis[105];
void init()
{int i,j;for(i=1; i<=n; i++){a[i].clear();vis[i]=0;dis[i] = MAX;}while(!q.empty()){q.pop();}
}
int main()
{int i,j,x,y,l;while(~scanf("%d%d",&n,&m)&&(n||m)){init();for(i=0; i<m; i++){scanf("%d%d%d",&x,&y,&l);node t1,t2;t1.x = y;t1.l = l;t2.x = x;t2.l = l;a[x].push_back(t1);a[y].push_back(t2);}dis[1] = 0;q.push(1);           //入队的是下标vis[1]=1;            //标记在队中while(!q.empty()){int s = q.front();q.pop();vis[s] = 0;x = s;for(i=0; i<a[s].size(); i++){y = a[s][i].x;if(dis[y]>dis[x]+a[s][i].l){dis[y]=dis[x]+a[s][i].l;if(!vis[y])q.push(y);}}}printf("%d\n",dis[n]);}return 0;
}

算法设计与分析第3章 贪心算法相关推荐

  1. 算法设计与分析第七章分支限界算法(完结篇)

    算法设计与分析第七章分支限界算法 一.分支界限算法概述 1.分支限界法类似于回溯法,是一种在问题的解空间树上搜索问题解的算法. 分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解 ...

  2. 算法设计与分析-TSP六种方法-贪心算法(最近邻点、最短链接)、蛮力法、动态规划法、回溯法、分支限界法、模拟退火

    旅行商问题,即TSP问题(Travelling Salesman Problem)又译为旅行推销员问题.货郎担问题,是数学领域中著名问题之一.假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径, ...

  3. 计算机算法设计与分析第五章思维导图知识点总结 ( 初稿 )

    复习链接 计算机算法设计与分析第一章思维导图 计算机算法设计与分析第二章思维导图&&知识点总结 计算机算法设计与分析第三章思维导图&&知识点总结 计算机算法设计与分析第 ...

  4. 算法设计与分析——十大经典排序算法二(6--10)

    一个不知名大学生,江湖人称菜狗 original author: jacky Li Email : 3435673055@qq.com  Time of completion:2023.3.1 Las ...

  5. 算法设计与分析_[04] 天牛须算法设计思想分析

    原文链接: https://arxiv.org/abs/1710.10724​arxiv.org 算法实现: 首先,初始化参数 ,分别代表初始解,初始的搜索范围,以及更新步长,且通过原文我们知道: 在 ...

  6. 算法设计与分析第5章 回溯法(二)【回溯法应用】

    第5章 回溯法 5.2 应用范例 1.0-1背包问题 有n件物品和一个容量为c的背包.第i件物品的重量是w[i],价值是p[i].求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和 ...

  7. [XJTUSE 算法设计与分析] 第五章 回溯法

    第五章 回溯法 填空题会有代码填空,大题会手动回溯 学习要点 理解回溯法的深度优先搜索策略. 掌握用回溯法解题的算法框架 (1)递归回溯 (2)迭代回溯 (3)子集树算法框架 (4)排列树算法框架 5 ...

  8. 算法设计与分析基础 第一章谜题

    习题1.1 10.b 欧几里得游戏 一开始,板上写有两个不相等的正整数,两个玩家交替写数字,每一次,当前玩家都必须在板上写出任意两个板上数字的差,而且这两个数字必须是新的,也就是说,不能与板上任何一个 ...

  9. 算法设计与分析第四章作业

    程序存储问题算法描述: 贪心算法: 总是从可选程序中选择最小的试放入磁盘. 排序:对程序按大小升序排: 循环添加:从i = 1开始,选择排序后序列的第 i 个(即a[i],1 <= i < ...

最新文章

  1. 使用spdevelop进行数据库建模
  2. android升级gradle到3.4.1
  3. python笔记22-literal_eval函数处理返回json中的单双引号
  4. 【Redis】配置redis主从复制
  5. 成功打开华三模拟器后,创建设备完成却启动设备失败
  6. 今天说说OPPO——OPPO 实时数仓揭秘:从顶层设计实现离线与实时的平滑迁移
  7. 【Spring】Spring第一天 - 环境搭建、IoC 详解、DI 详解等
  8. 在mysql中建立聚簇索引_给我一分钟,让你彻底明白MySQL聚簇索引和非聚簇索引...
  9. 统计在从1到n的正整数中1出现的次数
  10. Solr学习总结(四)Solr查询参数
  11. dibbler-server安装配置
  12. Android的ListView长按监听器
  13. java 二进制是什么类型_Java基础类型与其二进制表示
  14. 使用JDBC+JSP分层实现新闻管理系统注册、登录功能
  15. write.csv()函数--R语言
  16. Wowza 4.3.0安装和使用 并联合海康摄像头(RTSP)实现直播推流
  17. 百胜也要做外卖?市场会重新回到三足鼎立吗
  18. 星星之火OIer:NOIP2018完爆总结
  19. 不求星光灿烂,但愿岁月静好
  20. 微信链接防拦截跳转系统 微信域名防屏蔽技术

热门文章

  1. Dokcer启动2个mysql容器
  2. 这样的独处,能让你变得越来越优秀
  3. 【Sql Server】DateBase-SQL安全
  4. 假设检验_python 重点
  5. Google Colab 免费GPU服务器使用教程 挂载云端硬盘
  6. pip 指定目录安装
  7. OpenMP用法大全
  8. LeetCode简单题之解码字母到整数映射
  9. LeetCode简单题之删列造序
  10. Node.js 简单入门