2016年蓝桥个人赛赛前总复习 个人经验总结

来源:互联网 发布:手机怎么淘宝购物 编辑:程序博客网 时间:2024/05/16 10:57

20号就是蓝桥杯的省赛了,准备了半年,现在进入了最后的准备阶段,把几大经典算法和一些C++ 上必备的技巧做一个总结。

第一,dijkstra。为什么从dijkstra说起,因为这是最经典,最基础,使用率最广的图算法之一。

void dijkstra(){int path[v+1];int shortest[v+1]int shortestPoint;int shortestDist;int already;bool ifVisited[v+1];for(int i=1;i<=v;i++){path[i]=1;ifVisited[i]=false;shortest[i]=g[1][i];}shortestDist=0;shortestPoint=1;shortest[1]=0;ifVisited[1]=true;already=1;while(already<=v){shortestDist=inf;for(int i=1;i<=v;i++){if(ifVisited[i]==false&&shortest[i]<shortestDist){shortestPoint=i;shortestDist=shortest[i];}}ifVisited[shortestPoint]=true;already++;for(int i=1;i<=v;i++){if(ifVisited[i]==false&&g[shortestPoint][i]+shortest[shortestPoint]<shortest[i]){shortest[i]=g[shortestPoint][i]+shortest[shortestPoint];}}}return;}
基本数据结构如下:

path数组,记录这个点的前驱顶点是什么;

shortest数组,这个就是最终的结果,记录每个点的最短距离。

shortestPoint,当前回合择优选取的点。

shortestDist,当前回合选择最优点时,已经产生的最短距离。

ifVisited数组,记录哪些点已经达到了最短距离。

already,记录目前已经有几个点达到了最短距离。


第二,bellman-ford算法,这个算法用于dijkstra不能求的带负权的回路中。

代码如下:

void bellman-ford(){class line{public:int start,end,id,length;};line l[lineNumber+1];int leng[v+1];for(int i=1;i<=v;i++){leng[i]=inf;}leng[1]=0;for(int i=1;i<=v-1;i++){for(int i=1;i<=lineNumber;i++){if(leng[l[i].end]>leng[l[i].start]+l[i].length){leng[l[i].end]=leng[l[i].start]+l[i].length;}}} for(int i=1;i<=lineNumber;i++){if(leng[l[i].end]>leng[l[i].start]+l[i].length){cout<<"error";return;}}return;}

注意事项:bellman-ford是遍历边,遍历的循环次数是v-1次,v是点的数量,不是边的数量。

如果是无向图,则遍历边的时候,一条边遍历两次,相当于两条边,只是起点和重点的次序颠倒了。


第三:flyod算法。最直观的最短路算法,适用于要求任意两点间最短距离的情况,如果只需要求某一点到其他点最短路,则这个算法比较费时间,不推荐。

代码如下:

void flyod(){for(int i=1;i<=v;i++){for(int j=1;j<=v;j++){for(int k=1;k<=v;k++){if(g[j][k]>g[j][i]+g[i][k]){g[j][k]=g[j][i]+g[i][k];}}}}return;}

没有太多的注意事项。


第四:最小生成树 prim算法。

最简单的最小生成树算法。

代码如下:

void prim(){bool ifVisited[v+1];int minPoint,minDist;int minLength[v+1];int result=0;for(int i=1;i<=v;i++){ifVisited[i]=false;minLength[i]=inf;}ifVisited[1]=true;minPoint=1;minLength=0;for(int i=1;i<=v-1;i++){for(int j=1;j<=v;j++){if(ifVisited[j]==false&&g[minPoint][j]<minLength[j]){minLength[j]=g[minPoint][j];}}minDist=inf;for(int j=1;j<=v;j++){if(ifVisited[j]==false&&minLength[j]<minDist){minDist=g[minPoint][j];minPoint=j;}}result+=minDist;ifVisited[minPoint]=true;}return;} 


仔细看,prim和dijkstra真是很神似,他们的区别在于,prim在选择哪个点作为下一个点的时候,比较的是上一个选点和当前遍历点这跳线的长度哪个最短。而dijkstra是上个选点的最短路径+两点间距离。实际上这两个算法本质上是相同的。


第五:kruskal算法。

我们把最小生成树的算法放到一块来,一般来说,如果比赛中发现存储数据比较适合用边来的话,建议用kruskal,如果适合用一般的二维数组表示图,则使用prim比较方便写代码。

int father[e+1];class edge{public:int start,end,leng;};int cmp(const void *a,const void *b){return *(edge*)a.leng<*(edge*)b.leng;}int find(int a){return a==father[a]?a:find(father[a]);}bool join(int a,int b){a=find(a);b=find(b);if(a==b){return false;}else{father[a]=b;}return true;}void kruskal(){int result=0;edge ed[e+1];ed[1].leng=-inf;qsort(ed,e+1,sizeof(ed[0]),cmp);for(int i=1;i<=v;i++){if(join(ed[i].start,ed[i].end)==true){result+=ed[i].leng;}}return;}


第六:最小点覆盖,最大匹配数,匈牙利算法。

又一大比赛中常常出现的算法。最小点覆盖=最大匹配数。匈牙利算法基本代码如下:

bool ifVisited[y+1];int ancestor[y+1];bool dfs(int a){for(int i=1;i<=y;i++){if(ifVisited[i]==false&&g[a][i]==true){ifVisited[i]=true;if(ancestor[i]==0||dfs(ancestor[i])==true){return true;}}}return false;}void xiongyali(){for(int i=1;i<=y;i++){ancestor[i]=0;}for(int i=1;i<=x;i++){for(int j=1;j<=y;j++){ifVisited[j]=false;}if(dfs(i)==true){ans++;}}return;}


另外,两大公式:

最大匹配数=最小点覆盖;

最小路径覆盖=拆点前点数量-最大匹配数/2;


第七:0-1背包,最短两段子序列的动态规划。

拿0-1背包和子序列来说事,是因为这两个问题是动态规划的启蒙问题,具有非常高的代表性。

首先说一下0-1背包问题,典型解法:

for(int i=0;i<=itemNumber;i++){dp[0][i]=0;}for(int i=1;i<=itemNumber;i++){for(int j=1;j<=itemNumber;j++){dp[i][j]=dp[i-1][j]>dp[i-1][j-1]+weight[j]?dp[i-1][j]:dp[i-1][j-1]+weight[j];}}

初始化dp[0][n];然后从i开始遍历。


两段最短子序列问题,这个问题涉及三个数组,分别为前i个字符包括i组成的最大连续序列长度,后i个字符包括i组成的最大连续序列长度,前i个字符可以组成的最大序列,不包括i:

dp[0]=0;a[100];b[100];for(int i=1;i<=n;i++){dp[i]=dp[i-1]+value[i]>value[i]?dp[i-1]+value[i]:value[i];}a[0]=0;for(int i=1;i<=n;i++){if(a[i-1]>dp[i]){a[i]=a[i-1];}else{a[i]=dp[i];}}b[n]=0;for(int i=n-1;i>=0;i--){b[i]=value[i]>b[i+1]+value[i]?:value[i]:b[i+1]+value[i];}result=-inf;for(int i=0;i<n;i++){if(a[i]+b[i+1]>result){result=a[i]+b[i+1];}}return result;

第八:最大流算法,ek

最大流是用ek做,当然也可以km,ek比较直观,好上手,在比赛中比较合适。

int flow[v];int former[v];bool ifVisited[v];bool bfs(){queue<int> q;while(!q.empty()){q.pop();}for(int i=1;i<=v;i++){flow[i]=0;former[i]=0;ifVisited[i]=false;}former[1]=1;flow[1]=inf;ifVisited[1]=true;push(1);while(!q.empty()){int cur=q.front();q.pop();for(int i=1;i<=v;i++){if(ifVisited[i]==false&&g[cur][i]>0){ifVisited[i]=true;flow[i]=flow[cur]>g[cur][i]?g[cur][i]:flow[cur];push(i);}}}if(flow[v]==0){return false;}else{retur true;}}int ek(){bfs();int min=flow[v];while(min>0){int c=v;while(former[c]!=1){g[former[c]][c]-=flow[v];g[c][former[c]]+=flow[v];}result+=flow[v];bfs();min=flow[v];}return result;}


九,排序算法

直接插入排序,希尔排序,堆排序,冒泡排序,快速排序,归并排序




2 1
原创粉丝点击