• 算法心得总结


    很久以前搞算法的总结,罗列了一些很基本的算法。(深的我也不会……)

    每种算法都只有几句话,但都是最核心的步骤和思想。

                                               NO.1 贪心算法
    1 在对问题求解时,总是作出在当前看来是最好的选择。也就是说,不从整体上加以考虑,它所作出的仅仅是在某种意义上的局部最优解(是否是全局最优,需要证明)。

    2 基本步骤:
    1、从问题的某个初始解出发。
    2、采用循环语句,当可以向求解目标前进一步时,就根据局部最优策略,得到一个部分解,缩小问题的范围或规模。
    3、将所有部分解综合起来,得到问题的最终解。

    例题1:          活动安排问题
    hd 2073今年暑假不AC  ,2037

    思路:
    最短序列肯定包含结束时间最早的节目,然后从那个结束时间开始再找结束时间最早的.


    例题2:          区间覆盖问题
    用i来表示x轴上坐标为[i-1,i]的区间(长度为1),并给出M(1=<M=<200)个不同的整数,表示M个这样的区间。现在让你画几条线段覆盖住所有的区间,条件是:每条线段可以任意长,但是要求所画线段之和最小,并且线段的数目不超过N(1=<N=<50)。
    从N=1开始思考,逐渐增加N,考虑从哪儿断开即可.(就是从线段的间歇考虑)

    例题3:        哈夫曼编码(最小堆)
    hdu 1053


    例题4:        单源最短路径(Dijkstra算法)  2066
    基本思想是,设置顶点集合S并不断地作贪心选择来扩充这个集合。一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知。
    for( i = 1;i <= n-1;i++ )
    {
        id = choose();                                      //从s集中找到某个点,这个点发出的路径是与s集邻接的路径最短的一条                                                                                                          
        used[id] = true;                                      //将其纳入s集
        for( j = 1;j <= n;j++ )
        if( dis[id] + mat[id][j] < dis[j] && ( !used[j] ) )   //更新源点到各点之间的距离
        {
            dis[j] = dis[id ] + mat[id][j];                   //mat[][]为邻接矩阵,dis[i]为从指定点到i点的距离

        }
    }

    例题5:        求最小生成树     1863  1233
    (1)  Prim算法
    (2)  Kruscal算法(并查集)


    附录:  贪心算法和动态规划的比较.
       贪心法和动态规划的条件都有最优子结构性质,但还是有所不同的:
     例 背包问题: 给定n种物品和一个背包。物品i的重量是Wi,其价值为Vi,背包的容量为C。应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
            (1)不能将物品i装入背包多次,也不能只装入部分的物品i。
            (2)不能将物品i装入背包多次,可以只装入部分的物品i。

    答案:(2)可以用贪心算法解决,但(1)要用动态规划解决。

    (2)首先计算每种物品单位重量的价值Vi/Wi,然后,依贪心选择策略放入。

    (1)贪心选择之所以不能得到最优解是因为在这种情况下,它无法保证最终能将背包装满,部分闲置的背包空间使每公斤背包空间的价值降低了。事实上,在考虑0-1背包问题时,应比较选择该物品和不选择该物品所导致的最终方案,然后再作出最好选择。由此就导出许多互相重叠的子问题。这正是该问题可用动态规划算法求解的另一重要特征。


                                             NO.2 求a的b次方的方法
    用函数function(a,b)
    double function( a,b )
    {
        int ret = 1;
        while( b>0 )
        {
            if( b%2 == 1 )  //基本思想就是从b中提出2来先做a的次方.模2余1的在后面补上a相应的次方
                ret *= a;
            b /= 2;
            a *= a;
        }
        return ret;
    }
    一般来说结果都较大难以表述,题目求的往往是结果%某个数m的值。将上程序作一点小改动
    用函数function(a,b)
    int function( a,b,m )
    {
        int ret = 1;
        while( b>0 )
        {
            if( b%2 == 1 ) 
                ret = ret * a % m;
            b /= 2;
            a = a * a % m;
        }
        return ret;
    }

                                        NO.3 不定义tmp变量交换a和b的值

    void swap(int &a, int &b){

      b = a - b;

      a = a - b;

      b = a + b;

    }

    当然这样做的缺点是,如果a-b超出整形范围就比较麻烦了。

    这个时候改用

    void swap(int &a, int &b){

      a = a + b;

      b = a - b;

      a = a - b;

    }

    再geek一点?

    void swap(int &a, int &b){

      a = a ^ b;

      b = a ^ b;

      a = a ^ b;

    }

    这样做就没有正负和越界问题了。

                                
                                        NO.4 c/c++中写结构体的构造函数 和 重载运算符

    struct AA
    {
    public:
    int a;
    int b;
    private:
    int a;
    int b;
    protected:
    int GetA() const;
    void SetA();
    public:
    int GetB() const;

    AA & operator=(const AA & a);
    public:
    AA();
    AA(const AA & a);
    };

    可以看到struct和class没有区别,唯一的区别是,如果没有写public、private等,struct缺省是公有成员,class缺省是私有的。


                                 NO.5  KMP算法失效函数f()代码(两个字符串a,b返回a中第一次出现b的下标)

    fail[h]为string[h]的标记                          
    void f()
    {
    fail[0] = -1;
    for( i = 1;i < l;;i++ )
    {
         j = fail[i-1];
         while( p[j+1] != p[i] && j >= 0 )
         {
              j = fail[j]; 
         }
         if( p[j+1] == p[i] )
             fail[i] = j+1;
         else
             fail[i] = -1;
    }

    }

                                       NO.6     动态规划


    1.   0-1背包问题

      令m(i,j)是还有i,i+1,i+2......n个物品要放,背包空间为j时的最大价值

    于是:若 j - wi >= 0, m(i,j) = max(  m(i+1,j) , m(i+1,j-wi)+vi  );

         若 j - wi < 0,m(i,j) = m(i+1,j);
                              


    2.   最长子序列问题
      和01背包问题大致相同,p[i][j]记录a串的前i和b串的前j项内的最大子序列数,若a[i]==b[j],p[i][j] = p[i-1][j-1];
    若不等,p[i][j] = max { p[i][j-1],p[i-1][j] }

    3    数串最大和矩阵最大和.
    数串:b[i]为以a[i]结尾的和最大的子串,b[i] = max{a[i],b[i-1]+a[i]}
    (b[i]无需记录的)sum = b[i],i递增,不断更新sum,将最大的sum作为答案.
    矩阵:关键在于选子矩阵,对于列已经定了的子矩阵,可以用和求数串最大的方法,求出使得和最大的行,然后从所有可能的列组合中选出最小的即可。

                                       NO.7  博弈问题入门

     任何博弈问题(Impartial Combinatorial Games)(ICG)都可以转化为有向图问题。图上每一个节点代表一种局面,每一个节点都有一个SG函数值,一般末节点(游戏结束的局面)的SG函数值由题目推出,其余节点x的函数函数值满足sg(x) = mex{sg(y1),sg(y2),sg(y3)……}其中y1,y2,y3……为x的所有后继,mex{A}表示取不包含在集合A内的最小正整数。
      例如NIM游戏,可以看作是有n个棋子,在有向图上。所有棋子都到终点为胜。算法:可以转化为有n一样的张图,每张图一个棋子。
    某一点sg函数即是所有图上这点的sg函数值的异或。(^@^)
     

                                      

                                       NO.8 递推问题之错排问题
     编号为 1 , 2 ,……, n 的 n
    个元素排成一列,若每个元素所处位置的序号都与它的编号不同,则称这个排列为 n
    个不同元素的一个错排。   记 n 个不同元素的错排总数为 f(n) ,则
        f(n) = n![1-1/1!+1/2!-1/3!+……+(-1)^n*1/n!]

    下面用递推的方法推出这个公式:

    n 个不同元素的一个错排可由下述两个步骤完成:

    第一步,“错排” 1 号元素(将 1 号元素排在第 2 至第 n 个位置之一),有 n - 1
    种方法。

    第二步,“错排”其余 n - 1 个元素,按如下顺序进行。视第一步的结果,若 1
    号元素落在第 k 个位置,第二步就先把 k 号元素“错排”好, k
    号元素的不同排法将导致两类不同的情况发生:( 1 ) k 号元素排在第 1
    个位置,留下的 n - 2 个元素在与它们的编号集相等的位置集上“错排”,有 f(n -2)
    种方法;( 2 ) k 号元素不排第 1 个位置,这时可将第 1 个位置“看成”第 k
    个位置,于是形成(包括 k 号元素在内的) n - 1 个元素的“错排”,有 f(n - 1)
    种方法。据加法原理,完成第二步共有 f(n - 2)+f(n - 1) 种方法。

    根据乘法原理, n 个不同元素的错排种数

    f(n) = (n-1)[f(n-2)+f(n-1)] (n>2) 。


     

  • 相关阅读:
    解析 AJAX 返回回来的 xml字符串
    JS 与 后台如何获取 Cookies
    鼠标上下滚轮事件
    MVC Control 返回各种数据
    ildasm 查看程序集 里面的图标的意思
    对象的序列化和反序列化 itprobie
    文件上传通用类 itprobie
    文件下载的四种方式 itprobie
    委托事件的实际运用 itprobie
    使用NPOI实现excel的导入导出 itprobie
  • 原文地址:https://www.cnblogs.com/felixfang/p/1453557.html
Copyright © 2020-2023  润新知