ACM课程总结



当我仍是一个被P哥哥忽悠来的无知少年时,觉得编程只有C语言那么点东西,半个学期学完C语言的我觉得天下无敌了,谁知自从有了杭电练习题以后,才发现本身简直就是渣渣……咳咳进入正题:html

STL篇:

成长为一名绝世高手,入门是必须的,呐,入门第一步,ACM程序设计书一本,里面记载了STL各类花式招数,实战之中方便快捷,实在是各武林人士入门必选~自此我等一行数十人踏上了课程练习这一不归路,这次训练惨烈程度异常,由于正值寒假因此各位师兄弟被过年这一障碍干扰,没法专心修炼,通过了半个寒假的闯关我终于通关,可是没想到还有下一层关卡在等着咱们。算法

模拟篇:

模拟,特别是大模拟,在实战中是最为头疼的了,代码长,小数据多,可是这时候刚刚接触ACM的我并不知道这是模拟,起初我还觉得这是给咱们的水题,可是天真的我后来发现——我滴妈耶,这是个什么东西,模拟下棋,模拟打牌,模拟发牌,模拟生物实验,模拟……天哪出题人有这么无聊么,几百行代码,一不当心哪里错了又得一点点的调真是头疼,模拟最大的陷阱就是,题意很清楚,叫你很是想写,可是就是很麻烦让你又不很想写,纠结啊。可是平时练习嘛。编程

今后立志开学前将这套题写完的我,开始了没日没夜的撸代码日子,可是一天一个题的速度实在是太慢了,直到开学也没能把这套题写完;数组

贪心篇+二分三分:

开学以后掌门人正式的开班收徒了,第一个专题就是贪心,另外辅助点的东西就是二分三分;优化

贪心:

贪心算法就跟名字同样解决问题必定要担忧,拿田忌赛马这个例题来说,他和大王赛马的原则就是不能吃亏,能赢的就要赢,赢不了就要用本身最次的马和你最好的马跑,浪费掉你最好的马。编码

贪心的通常过程就是:url

(1)    将所给数据按照必定的顺序进行排序;spa

(2)    从头开始进行处理问题;设计

二分三分:

二分可用于具备单调性的直线求零点,经过中间值与两段的值的比较不断地缩小范围就能够在必定的精度范围内求出解。通常的解体模板为:code

While(abs(left-right)>1e-6)

{

         Mid=(left-right)/2;

If(F(mid)>0)

    Left=mid;

Else

    Right=mid;

}

 

三分可用于曲线求极值,通常模板为:

while(fabs(left-right)>1e-6)//三分来判断最大的那个角度

{

         mid=(left-right)/2;

         midmid=(mid+left)/2;

         if(F(mid)>F(midmid))

                  right=midmid;

         else

                  left=mid;

}

搜索篇:

一入搜索深似海,不知何年是归期,^0^万能的搜索没有点空间想象能力学这个真费劲。

这是一个心酸的故事,你可能不信,一开始我是拒绝的,5555555.没有什么问题是搜索解决不了的,若是有那就写两个搜索。学搜索开始的日子老是难熬的,广搜还好点,深搜就费劲了,递归没学明白,这个地方更是稀里糊涂的,看了一遍遍代码仍是不明白,只能一遍遍的走程序,真是煎熬,深搜状态多,就拿联通块的问题来讲,每一步均可能搜出来四步,很难弄的,光写就会写不少,好容易的把这个弄明白了,可是又迎来了又一大难题——超时!!!!!得想尽办法来剪枝,好烦啊!!!

广搜:

 

先将树顶的元素压入栈中,将他全部的儿子都搜出来压入栈中,这个搜索的元素就没有价值了,就能够将它出栈,接着只要栈不空就继续刚才的操做,直到栈空了,或者你找到你想要的解了。

深搜

深搜这里就不放图了(一个图得弄很长时间好麻烦滴说),就是一条路径找到头,直到尽头就返回搜索结果。不是我懒!!!我不懒!!!深搜我就理解了这么多,哼~

记忆化搜索

记忆化搜索和DP有些相似,用数组记录下来搜索的状态,每次搜索获得的结果和上一个状态进行比较取最优的结果,惟一有点不一样的就是,DP通常是从后往前更新状态,可是记忆化搜索是从前日后更新状态。

 

 

 

 

动态规划篇:

上学期一次机缘巧合之下得知了,动态规划这一武林至高秘籍,但也只是皮毛。

那是一个只有openjudge的日子,基础题作了几十道,就开始找找第二页有没有能作的题,采药!,这个题只有几行,就是一个最标准的01背包问题,以后……就没有以后了,这是什么鬼,问P哥哥这题怎么作,他说动态规划,还给我讲了怎么写,DP[i][j]表示第i个物品时,存放j大的物品的最优价值……等等,这都是什么鬼,这是什么,彻底不明白他在说什么。回去看讲课视频动态规划,开始有那么一点懂了,可是接下来就有杭电练习题,就把这个放过去了。

幸亏咱们有动态规划这个专题,这一下可找到好东西了,好多招式,01背包,多重背包,彻底背包……(等等,怎么全是背包),很差意思先了解了解,还没看更多的东西。

01背包:

把这个过程理解下:在前i件物品放进容量v的背包时,

它有两种状况:

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

For(int j=v;j>=c[i];j--)

F[i][j]=max(F[i-1][v],F[i-1][j-c[i]]+w[i]);

第一种是第i件不放进去,这时所得价值为:f[i-1][v]

第二种是第i件放进去,这时所得价值为:f[i-1][v-c[i]]+w[i]

 

这个图清晰明了,每次更新状态都是i行和i-1行做比较,谁的状态更优就取哪个,最优解就是最后一个状态f[n][v];

彻底背包:

彻底背包:

伪代码:

for i=1..N

    for v=c[i]..V

        f[v]=max{f[v],f[v-c[i]]+w[i]}

for(int j=c[i];j<=v;j++)

max();

j=c[i]+1

代码:

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}

解析:这里的max中的两项就是当前状态的值了,为什么?
由于每种背包都是无限的。当咱们把i1N循环时,f[v]表示容量为v在前i种背包时所得的价值,这里咱们要添加的不是前一个背包,而是当前背包。因此咱们要考虑的固然是当前状态。

 

多重背包:

 

多重背包能够这么理解遍历到当前物品时若是物品的整体积小于背包的剩余体积那么就能够当作是一个01背包问题,若是大于的话就能够当作是一个彻底背包问题;这样多重背包问题就能解决了。

 

彻底背包还有一个插曲:二进制优化,这样能够大大的节省时间,由于任何数都能由几个二进制数和一个非二进制数组成,好比9=2*2*2+1;这样就能将彻底背包转化为01背包,将二进制数当作物品,取仍是不取,找到最有的解决方案

最长上升子序列:

给定一个数列:ans[]={1,7,3,5,9,4,8};

显然此序列的最长上升自学列为1348,长度为4

怎么求?

用一个数组DP[i]表示以ans[i]为结尾的子序列的最大长度,DP[i]中的每个元素对应每个状态。

ans[]:1  7  3  5  9  4  8

从头开始->  DP[0] :异地一数结尾的长度确定是1;因此DP[0]=1;

                          DP[1]:ans[1]=7,向前遍历,只有当ans[i]时,才能取DP[i]这个状态,但此时状态不必定是最有的,还须要向前遍历完,知道遍历到数组尽头,选出最长的一个状态来,DP[1]=最长状态+1

                          For(int i=0;i

                          {

                                   int maxn=0;

                                   for(int j=0;j<=I;j++)

                                   {

                                            If(ans[j]maxn)

                                            {

                                                     Maxn=dp[j];

                                            }

                                   }

                                   dp[i]=maxn+1;

                          }

                  以此类推,将以每一个数结尾的最优状态找出来,而后再找出最最优的状态;

并查集篇:

并查集就是合并查找,最短路径;

1)“建树”:通俗的建树,就是将全部联通的点用一个标记标记出来,就造成了一个“数”,例如在1-10这十个点中,1 3联通那么1 3的就能够用相对小的1来标记。

2)查找:想查找两个点是否是联通的时候,只须要找到两个点的根是否是同一个根,若是是那么就是联通,不是就不是联通。

狄克斯特拉算法(dijkstra):


1
24是四个定点其余的是距离,从24最直接的就是2-4,可是不是最近的,须要舒展一下2-1-4,这样只有8.因此才是最短的。这个过程就是狄克斯特拉算法。下面进入正题:

 

咱们这里定义图的编号为:

1 2 3

4 5 6

7 8 9

1:初始化的图,其中包含边的权值(耗时)。(这里图是有向图)。

2:肯定起点,而后向能直接走到的点走一下,记录此时的估计值:2 6 9.

3:找到距离起点最近的点,是正东边的那个点,这时候咱们耗费权值为2。而后咱们进行松弛操做,从起点到其东南方的点直接到的权值耗费为6,可是咱们经过刚刚选定的点,咱们找到了到这个点更近的方式,因此这个时候咱们说从起点到其东南方向的点的权值更新值从6变成了5。这个时候咱们就完成了第一次松弛操做。

4:依旧是找距离起点最近的点。而后松弛咱们发现这个时候从起点到其东南方的点的耗费权值从5又变成了4.这个时候咱们完成了第二个松弛。

以后的方式同上:选定距离起点最近的点v。而后经过点v进行松弛操做。咱们发现可以经过增长走到目的地方式的复杂度(多转弯)的方式咱们可以松弛掉权值,使得耗费的权值更小。
    
模板:

void Dij()//咱们这里起点为1号编码点。咱们这里的d[]表示从起点到这个点须要的权值。w[a][b]表示点a到点b这条边的权值.
{
 int i,j,k,v,tmp;
 memset(vis,0,sizeof(vis));
 for(i=1;i<=n;i++)
     d[i]=w[1][i];//
对应图不难理解,对于起点的初始化
 d[1]=0;
 vis[1]=1;
 for(i=1;i<=n;i++)//
控制链接点的次数,例如上图,九个点,就循环九次。
 {
  tmp=N;//
这里N表示无穷大。也就是图上的99.
  for(j=1;j<=n;j++)
  {
   if(tmp>d[j]&&!vis[j])
   {
    tmp=d[j];
    v=j;
   }
  }//
每次咱们都找到距离起点最近的点v
  vis[v]=1;
  for(k=1;k<=n;k++)//
而后进行松弛操做。

咱们这里的d[]表示从起点到这个点须要的权值//加以强调其含义。

{

    if(!vis[k])

   d[k]=min(d[k],d[v]+w[v][k]);

}

}

}

 

 

矩阵相乘:

 

这个是在DP专题中,斐波那契数列:直接用线性递推来求f[n]复杂度是O(n),更适合球全部的f[1]f[n];若是用矩阵乘法算法的话计算K^n只有O(log n)的复杂度,只须要求出某个特定的f[n]是就占优了;

 

数字乘矩阵:
将每一个位置的数字都与常数相乘

 

矩阵加矩阵:
将各个位置的数字相加

矩阵乘矩阵:


这个矩阵相乘怎么理解看了别人的博客才懂的,借鉴博客地址在文章开头;

具体的就是线性方程:



证实方法(借鉴):

老实说,从上面这种写法,已经能看出矩阵乘法的规则了:系数矩阵第一行的21,各自与 xy 的乘积之和,等于3。不过,这不算严格的证实,只是线性方程式转为矩阵的书写规则。

下面才是严格的证实。有三组未知数 xyt,其中 xy 的关系以下。

 

x t 的关系以下。

 

有了这两组方程式,就能够求 yt 的关系。从矩阵来看,很显然,只要把第二个矩阵代入第一个矩阵便可。

 

从方程式来看,也能够把第二个方程组代入第一个方程组。

 

上面的方程组能够整理成下面的形式。

 

最后那个矩阵等式,与前面的矩阵等式一对照,就会获得下面的关系。

 

矩阵乘法的计算规则,从而获得证实。

具体怎么用呐:

用于递推中,能够一程度的减时;

例如:斐波那契数列,f[n]=f[n-1]+f[n-2];

f[0]=0;

f[1]=1;

 

用两个矩阵模拟出这个递推关系,就能够了;

相关文章
相关标签/搜索