您的位置:首页 > 其它

暑假ACM集训报告总结

2017-08-25 19:08 176 查看
        在这个炎热而忙碌的夏季,我们进行了一场二十多天的ACM集训。其实从大一上学期接触ACM来,我都对这个比赛充满了兴趣,对于AC一道题充满了喜悦。通过一个学期的学习,我掌握了一些基本的ACM算法知识,并能够解决一些简单的题。当听说暑假可以参加集训,我就马上报名参加了这次集训,也收获了很多东西。

在集训刚开始的时候,我们还不适应,老师就让我们复习一下搜索和图论的知识,并且做了一些搜索和图论的题。练习一下编程能力和熟悉一下做题的节奏。

通过复习的练习,大约巩固了一下关于搜索的知识点。

     1.深度搜索与广度搜索

深度搜索与广度搜索的控制结构和产生系统很相似,唯一的区别在于对扩展节点选取上。由于其保留了所有的前继节点,所以在产生后继节点时可以去掉一部分重复 的节点,从而提高了搜索效率。这两种算法每次都扩展一个节点的所有子节点,而不同的是,深度搜索下一次扩展的是本次扩展出来的子节点中的一个,而广度搜索 扩展的则是本次扩展的节点的兄弟节点。在具体实现上为了提高效率,所以采用了不同的数据结构.

      2.回溯算法

回溯算法是所有搜索算法中最为基本的一种算法,其采用了一种“走不通就掉头”思想作为其控制结构,其相当于采用了先根遍历的方法来构造解答树,可用于找解或所有解以及最优解。评价:回溯算法对空间的消耗较少,当其与分枝定界法一起使用时,对于所求解在解答树中层较深的问题 有较好的效果。但应避免在后继节点可能与前继节点相同的问题中使用,以免产生循环。

当然还有其他知识点就不一一介绍了。

学习图论的知识,巩固了下面的知识点。

     一、求出最短路径的长度

  以下没有特别说明的话,dis[u][v]表示从u到v最短路径长度,w[u][v]表示连接u,v的边的长度。

1.Floyed-Warshall算法 O(N3)

  简称Floyed(弗洛伊德)算法,是最简单的最短路径算法,可以计算图中任意两点间的最短路径。Floyed的时间复杂度是O (N3),适用于出现负边权的情况。

算法描述:

初始化:点u、v如果有边相连,则dis[u][v]=w[u][v]。

  如果不相连则dis[u][v]=0x7fffffff

For (k = 1; k <= n; k++)

For (i = 1; i <= n; i++)

For (j = 1; j <= n; j++)

If (dis[i][j] >dis[i][k] + dis[k][j])

dis[i][j] = dis[i][k] + dis[k][j];

算法结束:dis[i][j]得出的就是从i到j的最短路径。

2.Dijkstra算法O (N2)

用来计算从一个点到其他所有点的最短路径的算法,是一种单源最短路径算法。也就是说,只能计算起点只有一个的情况。

Dijkstra的时间复杂度是O (N2),它不能处理存在负边权的情况。

算法描述:

设起点为s,dis[v]表示从s到v的最短路径,pre[v]为v的前驱节点,用来输出路径。

a)初始化:dis[v]=∞(v≠s); dis[s]=0; pre[s]=0;

b)For (i = 1; i <= n ; i++)

1.在没有被访问过的点中找一个顶点u使得dis[u]是最小的。

2.u标记为已确定最短路径

3.For 与u相连的每个未确定最短路径的顶点v

if (dis[u]+w[u][v] < dis[v])

{

dis[v] = dis[u] + w[u][v];

pre[v] = u;

}

c)算法结束:dis[v]为s到v的最短距离;pre[v]为v的前驱节点,用来输出路径。

3.Bellman-Ford算法O(NE)

Bellman-Ford算法的思想很简单。一开始认为起点是白点(dis[1]=0),每一次都枚举所有的边,必然会有一些边,连接着白点和蓝点。因此每次都能用所有的白点去修改所有的蓝点,每次循环也必然会有至少一个蓝点变成白点。

虽然Bellman-Ford算法可以求出存在负边权情况下的最短路径,却无法解决存在负权回路的情况。

算法实现:

设s为起点,dis[v]即为s到v的最短距离,pre[v]为v前驱。w[j]是边j的长度,且j连接u、v。

初始化:dis[s]=0,dis[v]=∞(v≠s),pre[s]=0

For (i = 1; i <= n-1; i++)

For (j = 1; j <= E; j++)//注意要枚举所有边,不能枚举点。

if (dis[u]+w[j]<dis[v]) //u、v分别是这条边连接的两个点。

{

dis[v] =dis[u] + w[j];

pre[v] = u;

}

     二. 最小生成树

1.Prim算法采用与Dijkstra、Bellman-Ford算法一样的“蓝白点”思想:白点代表已经进入最小生成树的点,蓝点代表未进入最小生成树的点。

算法描述:

以1为起点生成最小生成树,min[v]表示蓝点v与白点相连的最小边权。

MST表示最小生成树的权值之和。

a)初始化:min[v]= ∞(v≠1); min[1]=0;MST=0;

b)for (i = 1; i<= n; i++)

1.寻找min[u]最小的蓝点u。

2.将u标记为白点

3.MST+=min[u]

4.for 与白点u相连的所有蓝点v

if (w[u][v]<min[v])

min[v]=w[u][v];

c)算法结束: MST即为最小生成树的权值之和。

2.Kruskal算法

算法描述:

初始化并查集。father[x]=x。

tot=0

将所有边用快排从小到大排序。

计数器 k=0;

for (i=1; i<=M; i++) //循环所有已从小到大排序的边

if 这是一条u,v不属于同一集合的边(u,v)(因为已经排序,所以必为最小),

begin

 ①合并u,v所在的集合,相当于把边(u,v)加入最小生成树。

  ②tot=tot+W(u,v)

③k++

④如果k=n-1,说明最小生成树已经生成,则break;

end;

6. 结束,tot即为最小生成树的总权值之和。

在集训期间还学习了一些新知识。

     一.单调队列。

单调队列:队列中元素之间的关系具有单调性,而且,队首和队尾都可以进行出队操作,只有队尾可以进行入队操作。。

单调队列的常用操作如下:

(1)插入:若新元素从队尾插入后会破坏单调性,则删除队尾元素,直到插入后不再破坏单调性为止,再将其插入单调队列。

(2)获取最优(最大、最小)值:访问首尾元素。

有一个区间求极值问题。

先枚举起始元素ax,然后求ax到ax+k-1的最大(小)值。我们得到了一个复杂度为o(nk)的算法。

怎么用单调队列求解那?

以最大值为例

对任意l<=i<j<=r,如果ai<aj,那么,在区间向右移动的过程中,最大值永远也不会落在ai上,因为ai比aj先失效,能用ai一定能用aj ,此时,我们便不再需要ai了。

这个性质似乎与单调队列的性质重合了。

当我们将区间从(l,r)移动到(l+1,r+1)时,我们将ar+1插入单调队列,若队首元素不在(l,r)区间当中,删除它。

       二.树状数组

首先要分清a[] c[] sum[] 他们各自所代表的意思;

a[]就是输入的数组;

c[]就是建立的树状数组;

c[i] = a[i - 2^k +1] + ...... + a[i];

a有多少个c就有多少个,而且c[i]肯定包含相应的a[i];

lowbit(i) = 2^k 表示i的二进制数表示形式留下左右边的1其余为取0得到的数

sum[k] = c[N1] + c[N2] + c[N3].......+ c[Nm];

Ni-1 = Ni - lowbit(i);

求和的话,就是有c[Nm] c[Nm-1] c[Nm-2] .... c[N1]的过程 Ni - lowbit(i)的过程是将Ni的二进制的最右边的1去掉的过程,所以求和的时间复杂度为O(log(k));

而更新也是如果a[i]更新了 那么c[N1] c[N2] ..... C[Nm]也要更新N1= i; 就是从 c[N1] c[N2] ... c[Nm]的过程 Ni = Ni-1 + lowbit(i);就是在Ni - 1的二进制最右边1后边填1的过程,就是在时间复杂度也是O(log(n))级别。

树状数组的基本操作。

1.区间更新,单点求和。

当[a,b]区间加1时。

可以 add(a,1);从这之后的sum()都会加1。

add(b+1,-1); 从这之后的sum()都会减1。也就是从这之后原来的加一被抵消了。

这样原来的求和数组已经不是原来的意义了,这已经不是为了求和了。

这时候在区间[a,b]内求一个值sum(i)会比原先加1。

2.单点更新,区间区和。

当需要a[i]增加d时,

可以 add(a[i],d);

这样 当 x > i 时,sum(x)求和都增大了d。

求和时,求区间[ j , k ] 的和,sum(k)- sum(j-1);

3.离散化

离散化一般都要定义结构体,结构体里一般包含两个数,一个数是输入的原值 v ,一个数是记录输入的先后次序的 id。

1。在输入数据时,要便输入边对结构体的 id 进行赋值,从1到n。(输入的先后顺序)

2。根据结构体中数据的大小进行排序。(排序)

3。新建立一个数组b,结构体的 id 作为数组下表,然后,根据排序的先后顺序,给数组b赋从1到n的值代表他们原来的大小关系。(大小的代换)

离散化完毕,现在这些不集中的数据,已经变得集中了。在处理数据大,数据量不大的数据时可以采用离散化。

在我们学习中遇到的这些算法的类型还有很多,这里就不一一写下来了,在这里出现的都是一些算法的基础内容和重要知识点。这些我们一定要掌握牢固,毕竟只有基础打得牢,楼房才能建的好。

以上就是我们暑假集训的学习成果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: