算法心得总结

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

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

 

                                           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) 。

 

 

 

 

 


 

 

posted on 2009-05-10 10:57  Felix Fang  阅读(871)  评论(1编辑  收藏  举报

导航