数据结构与算法笔记

算法 数据结构 问题 程序的关系

  问题由模型作为内核,用替换方式将抽象概念具体化,形成描述来包装内核
 
模型由数据和解决方法构成,每个问题不同情况的表现组成成数据,通过统一的解决方法得到解决
 
算法是解决问题的一般化方法
 
数据结构是数据的组织形式
 
程序是用计算机语言,在数据结构上操作算法,利用计算机解决问题

数据结构
 
数据结构是独立于数据而存在,具有一定逻辑结构,支持在上面进行被允许的操作,如插入、删除、查找等

  主要有 单元节点、散列、线性表、树、图

单元节点/元素
 
最基本元素,储存数据的直接单位

散列
 
元素相互独立,没有直接关系的结构 集合
 
唯一关系 元素在集合内

  操作 插入 删除 查找某元素
 
集合的实现 数组 BST

  Hash
   
考虑集合的数组表示a[x]x是否在集合中,这是一一对应的关系
    Hash
表将x用特征值t来表示对于不同的x可能对应同一个t(即冲突),根据t来查找x
    f(x) = t
的函数是hash函数, txhash

   
因为可能存在冲突 有不同的处理方法 通常用链表来存储不同的x
   
操作 插入 删除 查找

   
先计算f(x) 在对应的链表中操作, 好的hash函数可以让链表尽可能短, 达到理想效果

    典型应用:记录庞大数字、字符串等,字典的实现

线性表
 
线性表中,元素线性排列,相邻元素有前驱后继的关系

  操作 插入 删除 查找某元素 遍历 查找第k
 
线性表实现 数组 链表

  有序表
   
元素顺序排列,可以用二分查找某个元素,可以直接获取第k小,插入删除困难,适用于排序之后的静态操作

 
   
先进后出,用于人工模拟递归操作,表达式处理,递归过程中获取解答和模拟类题目

  队列
   
先进先出,用于宽度优先搜索

  映射
   
在线性表上加以集合构造 a[]是一个数组,已经具备某种逻辑关系A
    b[i]
表示b[]i个元素在a中的位置,初始b[i]=i,通过对a[b[i]]的排序得到对应数组a[]具有逻辑关系Ba[]中元素位置未改变过

  指针操作
   
对于一个序列,在上面进行简单的指针移动,获得完全遍历求最优解

字符串
 
匹配
    RK
将模式串用数字表示(hash),然后通过移动将当前匹配单位的hash值计算出来,相等的时候,逐个判断
   
    KMP P
为模式串 S为正文,用R[p]表示最大的k 使得P[1..k] = P[m-k+1..m],这样如果在p+1处匹配失败,可以直接移动k个单位,匹配的过程实际为O(n+m)
       
获得R[]的过程,对于R[p]k = R[p-1]+1 如果P[k]不等于P[p]k移动到R[k-1]上,继续匹配,否则R[p]=kk右移

文章来源: http://thunderfyc.wordpress.com.cn 

==================================================================================

1 0..65535范围内的数字进行O(N)复杂度的排序

  t[i]表示等于i的元素有多少个 从0开始往后数即可

2 有一串数,找到两个的商最接近t

  将数排序之后 用两个指针l, r 如果a[l]/a[r] < t, 那么a[l+1]/a[r]可能更接近t, 至少比a[l]/a[r+1]接近;
 
反之亦然。最后lr1遍历到n之后,中途遇到的最优解即可

3 表达式处理

  表达式 = 表达式 符号 表达式。只有当表达式为优先级最高的时候,才能把它转化为数字,替换,这个就是典型递归过程,
 
但是串的处理是顺序的,所以用栈操作,每次判断新压入栈的符号是否更高级,如果更高级 将前面的基础表达式计算。

==================================================================================

Hash
  f_hash(x: dataType): int;
 
  find(x: dataType): node;
  {
    t = f_hash(x);
    s = hash[t];
    while (s.data <> x)
    {
      s = s.next;
      if (s = nil)
        return nil;
    }
    return s;
  }

  ins(x: dataType);
  {
    t = f_hash(x);
    s = hash[t];
    if (s = nil)
    {
      new(hash[t]);
      hash[t].data = x;
      hash[t].next = nil;
      return;
    }
    while (s.next <> nil)
      s = s.next;
    new(s.next);
    s = s.next;
    s.next = nil;
    s.data = x;
  }

  del(x: dataType);
  {
    if (find(x) = nil)
      return;
    t = f_hash(x);
    s = hash[t];
    if (s.data = x)
    {
      hash[t] = s.next;
      return;
    }
    p = s;
    s = s.next;
    while (s.data <> x)
    {
      p = s;
      s = s.next;
    }
    p.next = s.next;
    delete(s);
  }

RK
  RK(p:string, s:string): int;
  {
    pt = 0;
    m[0] = 1;
    for i <- 1 to p.len do
    {
      pt = (pt*26+p[i]) mod 29999;
      m[i] = m[i-1]*26;
    }
    st = 0;
    for i <- 1 to p.len do
    {
      st = (st*26+s[i]) mod 29999;
    }
    for i <- p.len+1 to s.len do
    {
      st = ((st - m[p.len]*s[i-p.len])*26+s[i]) mod 29999;
      if (st = pt)
      {
        check one by one;
        if ok
          return i;
      }
    }
    return -1;
  } 

KMP
  pre(p: string);
  {
    j = 1;    k = 0;    next[1] = 0;
    while (j < p.len)
    {
      if (k = 0 or p[j] = p[k])
      {
         j+1; k+1;
         if (p[j] <> p[k])
           next[j] = k
         else
           next[j] = next[k];
      }
      else
        k = next[k];
    }
  }

  KMP(p: string, s: string);
  {
    i = 1;  j = 1;
    while (j <= p.len and i <= s.len)
    {
      if (j = 0 or s[i] = p[j])
      {
        i+1; j+1;
      }
      else
        j = next[j];
    }
    if (j > t.len)
      return i;
    else
      return -1;
  }

文章来源: http://thunderfyc.wordpress.com.cn

By thunderfyc in 数据结构与算法

Jul 24

文章来源: http://thunderfyc.wordpress.com.cn


 
一个结点对多个结点的结构,具有清晰的偏序关系

  多叉树 好多孩子
 
二叉树 最多俩孩子

  无根树 没有固定的根的树 形态不固定 边无向
 
有根树 有固定根的树 节点之间有确定的父子关系

  互相转化
   
多叉树->二叉树 左儿子 右兄弟
   
无根树->有根树 确定根之后 遍历

  遍历
   
前序 中序 后序 宽度优先

  BST
   
左孩子比根小 根比右孩子小
   
易退化 为深度很深的树
   
解决办法
     
打乱顺序 将输入的数据随机排列之后建树
     
线段树   输入数据排序之后把看作长度为0的线段插入 或者已知数据范围 进行操作
      Treap
随机一个rank 使rank满足堆的性质 元素满足BST性质
     
红黑树 节点为红或黑 总有两孩子 红节点的孩子为黑 每条路径上黑节点个数相同
      AVL  
d表示左子树和右子树深度差维持在-1 0 1之间

  并查集
   
将一些元素归类 选出一个代表元素 指向这个代表元素
   
典型树结构 但是用数组实现 只记录节点父亲

 
   
根比儿子小的二叉树 用数组实现,dad*2 = leftson, dad*2+1 = rightson
   
能够迅速获取最小或者最大

  Huffman
   
编码树 对于给定的文章 对字符重新编码 使得总长度最小
   
到左儿子标记为0 到右儿子标记为1 访问次数越少的字符深度越大

================================================================================

1 0..65535范围内的数 插入 删除 可询问小于某个数的个数
  BST
或者线段树

2 不停的插入删除数 求第k小的数
  BST
或者线段树 或者堆
 
维护一个容量为K的堆大根堆 每次讯问的时候 返回堆顶;
 
读入一个数 如果比堆顶小 就把堆顶弹出 插入新数
 
如果比堆顶大 就无视

文章来源: http://thunderfyc.wordpress.com.cn

================================================================================

线段树
  BuildTree(l, r, t: int);
  {
    L[t] = l; R[t] = r; C[t] = 0;
    if (l == r)
      return;
    BuildTree(l, (l+r) div 2, t*2);
    BuildTree((l+r)div2 +1, r, t*2+1);
  }

  Ins(x, t: int);
  {
    if (L[t] = R[t])
    {
      C[t]+1;
      return;
    }
    if (x <= L[t*2])
      Insert(x, t*2);
    else
      Insert(x, t*2+1);
  }

  Del(x, t: int);
  {
    if (L[t] = R[t])
    {
      C[t]-1;
      return;
    }
    if (x <= L[t*2])
      Del(x, t*2);
    else
      Del(x, t*2+1);
  }

  Find(k, l, r, t: int): int;
  {
    if (r <= R[t*2])
      return Find(k, l, R[t*2], t*2);
    if (l >= L[t*2+1])
      return Find(k, L[t*2+1], r, t*2+1);
    if (k <= C[t*2])
      return Find(k, l, R[t*2], t*2);
    if (k > C[t*2])
      return Find(k-C[t*2], L[t*2+1], r, t*2+1);
  }

并查集
  Find(x: int): int;
  {
    if (sets[x] = x)
      return x;
    tmp = Find(sets[x]);
    return Find(sets[x]);
  }

  Union(x: int, y: int);
  {
    t = Find(x);
    s = Find(y);
    if (Rank[t] < Rank[s])
      sets[t] = s
    else
      sets[s] = t;
  }


  Del()
  {
    data[1] = data[n];
    n-1;
    dad = 1; son = 2;
    while (true)
    {
      if (son+1 <= n and data[son] > data[son+1])
        son+1;
      if (data[dad] > data[son])
        swp(data[dad], data[son]);
      dad = son;
      son = son*2;
      if (son > n)
        break;
    }
  }

  Ins(x: int)
  {
    n+1; data[n] = x;
    son = n; dad = n div 2;
    while (data[dad] <= data[son])
    {
      if (data[dad] > data[son])
        swp;
    }
  }

文章来源: http://thunderfyc.wordpress.com.cn


 
多个节点和多个节点之间的关系

  无向图 边可以双向行走
 
有向图 边只能单向行走

  边集数组 存储边的数组
 
邻接矩阵 g[i][j]表示ij之间连边
 
邻接表   g[i][j]表示i的第j个邻居

  DFS
   
从一个节点开始 沿着一条路径走 走不通退回 然后走另外一条

  BFS
   
从一个节点开始 访问每一个后继 然后再每个后继继续访问 用队列

  Euler
   
所有点的出度等于入度才存在 任意一个点 找后继插入 走一条边删一条边

   
直到回到这个点 这条路径记录下来 继续重复 直到没有边为止

  拓扑排序
   
有向图中 到达某个点 先要到达所有前驱 求访问顺序
    DFS
的时候 从某个点的递归层中 记录离开递归层的顺序 将顺序逆向输出

  强连同分量
   
有向图中 任意两个点都能连通的子图
   
DFS 记录下离开递归层的顺序 然后边全部反向 从最后一个开始依次DFS 所得到的每一棵DFS树都是一个强连同分量

  关节点/割顶
   
去掉之后 图就不连同的节点
    DFS
之后 如果根有2个以上的孩子 那么根是关节点
   
记录和当前节点相连的被访问到的节点中 非自己父亲 的深度最小的 记录下来 如果和当前节点相连的一个节点没有被访问过进行访问 访问完后
   
如果和孩子相连的祖先深度不比他小 这个节点就是关节点

 
   
去掉后 图就不连同的边
   
同求关节点 如果相连的祖先深度比父亲要大 这条边为桥
   

  最小生成树 将所有点连接起来的无根树 边为图的边 边权和最小
    Kruskal
每次找不在同一个集合的两个节点 连边最小 用并查集实现
    Prim
在没有被连接到的节点里找到一个和已经被连接的节点边权最小的 加入 用堆实现

  次小生成树
    Prim
同时记录max[u][v]表示从uv路径中最大的边权 枚举不在MST中的g[u][v]必然大于max[u][v]替换之 找到g[u][v]-max[u][v]最小的即可
    Kruskal
后枚举MST中的边 去掉后重新求MST 找其中最小的一个

  最短路
   
单源最短路径
      Bellman-ford
       
不停的选择d[i]+g[i][j]是否可行作n次迭代 可判断是否存在负环

      Dijkstra
       
每次找到某个点最近的点d[i] 然后做修改d[i]+g[i][j]

    任意点对最短路径
      Floyd
       
本质是矩阵乘法  Mi是经过长度为i的最短路径 Mi*M0 = M(i+1) 其中的操作i j kMi[i][k]+M0[k][j] < Mi+1[i][j]就更改

    差分约束系统
     
满足若干大小关系xi-xj<=bk
     
建图 T到所有点射出权为0的边 如果xi-xj<=bkji射出bk 最后0到其他节点的最短距离为一组解

      0
i的距离要小于等于到j的距离加上边的权值  显然是最短路

文章来源: http://thunderfyc.wordpress.com.cn

==============================================================================

DFS
  p = 1;
  DFS(k, dad, dep:int)
  {
    Ck = Grey; Dk = dep;
    //Ak = dep; tot = 0;

    for i <- 1 to n
    {
      // if (g[i][k] and i <> dad and Ci = Grey)
      //   Ak = min{Ak, Di}
     
      if (g[i][k] and Ci = White)
      {
        DFS(i, k, dep+1);

        //tot+1; Ak = min{Ak, Ai};
        //if ((k=root and tot>1) or (k<>root and Ai >= Dk))
        //  Cut_k = true
        //if (Ai > Dk)
        //  Bridge[k][i] = true
      }
      post[i] = p;
      p1;
      Ck = Black;
  }

Topsort
  for i <- n downto 1
    which post[j] = i;

Kruskal
  {
    sort all edges;
    for i <- 1 to m
    {
      if (find(a[i]) <> find(b[i]))
      {
         tree[i] = true;
         union(a[i], b[i]);
      }
    }
  }

Prim
  {
    for i <- 1 to n
    {
      for j <- 1 to n
      {
        if (ok[j] = false)
          heap_ins(g[i][j]);
      }
      ok[heap[1]] = true;
      tree[pre[1]][heap[1]] = true;
    }
  }

Bellman-Ford
  for i <- 1 to n+1
  {
    ok = false;
    for j <- 1 to n
      for k <- 1 to n
        if (d[j] + g[j][k] < d[k])
        {
          d[k] = d[j]+g[j][k];
          ok = true;
        }
    if (ok = false)
      break;
    if (i = n+1 and ok = true)
      return neg-cycle;
  }

Dijkstra
  for i <- 1 to n
  {
    min = inf; oper = -1;
    for j <- 1 to n
      if (d[j] < min)
      {
        min = d[j]; oper = j;    
      }
    if (oper = -1)
      return;

    for j <- 1 to n
      if (d[j] > d[oper] + g[oper][j])
      {
        d[j] = d[oper] + g[oper][j];
        pre[j] = oper;
      }
  }

Floyd

  Floyd
  {
    for k <- 1 to n
      for i <- 1 to n
        for j <- 1 to n
          if (d[i][j] > d[i][k]+d[k][j])
          {
            d[i][j] = d[i][k] + d[k][j];
            p[i][j] = k;
          }
  }

  GetPath(i, j): string
  {
    if (p[i][j] = 0)
      return ij;
    s = GetPath(i, p[i][j]);
    s - p[i][j];
    s + GetPath(p[i][j], j);
    return s;
  }

  Matrix Floyd
  {
    M[1] = G;
    for x <- 2 to n
    {
      for i <- 1 to n
        for j <- 1 to n
          for k <- 1 to n
          if (M[x][i][j] > M[x-1][i][k] + G[k][j])
            M[x][i][j] = M[x-1][i][k] + G[k][j];
    }
  }

文章来源: http://thunderfyc.wordpress.com.cn

枚举
 
状态的简单枚举
   
可以简单表示的单参数状态,对每个状态计算得到结果判断是否相等

  状态压缩表示
   
将状态压缩表示 如一组数 选择与不选用01表示 然后枚举

  对已知答案集的枚举
   
知道答案的范围 然后对答案进行枚举判断是否符合条件

  步长叠进枚举
   
知道答案 并且答案对情况的影响连续变化 通过大步寻找小范围 然后小步寻找答案的方法解 通常为几何问题

贪心
 
DP类似 满足最优子结构 同时满足最优选择 即能导出最优子结构的选择唯一可判定

分治 二分
 
将问题分解为子问题 子问题共同对问题产生贡献
 
答案二分 单调决策序列二分

  归并排序
   
将序列拆为一个元素长的若干序列 然后合并的时候将相邻的序列合并 最后合并为一个大的

  快速排序
   
选定一个轴将小的放在左边 大的放在右边 然后分别继续处理两边

  二分解方程
   
确定一个范围 满足f(l) f(r)异号 然后计算mid=(l+r)/2 找到f(l) f(r)中和f(mid)异号的 缩小范围 继续

  LIS的二分优化
   
找到一个序列中的最长不下降序列
   
记录t[x]为长度为xLIS的最后一个元素大小 要尽可能的小 可见t[]是有序的数组
   
每插入一个a[i] 如果a[i]比某个t[k]小 比t[k-1]大 那么a[i]替换这个t[k] 满足条件 如果比t[x]要大 t[x+1]a[i] 整个序列的LIS拉长一位

文章来源: http://thunderfyc.wordpress.com.cn

=============================================================================

8×8棋盘上八皇后摆放 棋盘每格有分数 最后八皇后占据格子分数最大
求出所有摆放方案 枚举方案 计算

三个食物有不同的营养成分 选取每个食物的份数 最后让营养成分成要求的比例
枚举每个食物的份数 计算

已知若干线段 找一点 连接到这线段上某一点 使得距离和最小
连续变化 用大步长确定范围 小步长确定位置

若干活动 知道起始时间 求不冲突活动的最大个数
按结束时间排序 然后如果mi之后开始 j前结束 的结束时间最小的 那么m必选 i m之间没有其他活动

若干活动 知道起始时间 求安排所有活动的最少礼堂数
将开始和结束时间并在一个表里排序 开始的时候找到一个空闲的礼堂插入 如果未使用过 礼堂数+1 结束的时候把该礼堂设置为空闲放入空闲礼堂对首

序列AB 要求Pi(ai^bi)最大的排序方案
降序排列  bi>=bj ai>=aj ai^(bi-bj) >= aj^(bi-bj) ai^bi*aj^bj >= ai^bj*aj^bi

文章来源: http://thunderfyc.wordpress.com.cn

递归
 
将问题转为子问题 解决 降低编程复杂度使用

搜索
  DFS
   
每层递归枚举一个选择 然后继续递归 得到所有解

  BFS
   
将队首的节点扩展之后放入队尾 然后继续扩展 直到没有节点为止

    Floodfill
     
要判断一个格点盘上连通的格点标记

     
从一个格点开始 将其放入队首 扩展周围邻居格点 如果没访问过 放入队尾 然后继续扩展直到不可扩展为止

搜索优化
  Hash
判重
   
判重的时候利用Hash可以O(1)判定是否访问过

  搜索顺序
   
有的时候从小到大 有的时候从大到小 有的时候根据选择评估得到解得可能性

  双向BFS
   
将初始状态扩展 目标状态扩展 两者重合时结束 得到一条路径

  分支定界
   
扩展节点对于超过可行范围的砍掉
随机化
 
利用随机打乱输入限制 尽可能得到平均效率

  生成随机序列
   
对于P[i] P[i+1]P[n]中随机找一个 和P[i]交换

  搜索过程中随机选择
 

  QuickSort随机选择中枢点

文章来源: http://thunderfyc.wordpress.com.cn

DP
 
把问题分化为子问题之后 取之最优解 从底向上 或用递归从上往下再往上

  最优子结构
   
能从最优的子问题解答中解答主问题
   
最优子结构的解需要独立 一个问题的解不会影响到另外一个问题

  重叠子问题
   
两个大问题的若干子问题中 有相同的
   
注意存储子问题的解 以提高效率

  重建最优解
   
在解决子问题的时候 记录取得最优解的选择 可以从根回溯到解

  记忆化
   
从主问题递归子问题 子问题返回的解记忆下来 以后再子问题的时候可以直接调用
   
既延续从底开始的不需重复计算的优点 也避免了从底开始会进行很多不必要计算的缺点

记忆化搜索
 
在搜索过程中 记录下子问题的解 然后如果搜索到这一步骤 就直接取得解
  DP
的一种表现形式

=====================================================================
8
皇后

  
记录覆盖线的编号 斜边按中间为0 两边为正负的方法记录
  
利用对称性

Castle
 
已知图上城堡用颜色染色 然后判断有多少城堡 最大城堡多大

  Floodfill

8数码
 
一个9宫格上8个数字 有一个空格 每次可以把一个数字移到空格上  要把它排成预定样子
  A*
或者双向BFS
矩阵连乘的计算代价可以用结合律的方法来减小 求最少计算代价 计算代价为乘法的次数

     
结合的种类数为 Pn = Σ(Pk*P_n-k) P1 = 1
     
opt[i][j]表示从ij的计算代价 opt[i][j] = min{opt[i][k] + opt[k+1][j] + pi-1*pk*pj} p表示列

     
另外可用s[i][j]表示取得最优解的k
     
用从底向上的方法计算的话 就是
for l = 2 to n; for i = 1 to n-l+1; j=i+l-1; for k = i to j-1;
      l
是枚举当前处理的区间长度 i是左端点 j是右端点 k是最优解选择 很显然 opt[i][k] opt[k+1][j]因为区间长度小于opt[i][j] 所以必然已经解出

已知两序列 求最长的公共子序列(不一定连续)
     
如果xm = yn zk=xm=yn 而且z(1..k-1)xm-1 ym-1
LCS
     
如果xm<>yn 如果zk=xm 那么ZXYn-1LCS 如果zk=yn 那么ZXm-1Yn
LCS
     
     
c[i][j]表示X到第i Y到第j的最长长度
  c[i][j] = c[i-1][j-1]+1 [xi=yj] c[i][j] = max{c[i][j-1], c[i-1][j]} [xi <> yj]
     
可以用b[i][j]表示是从哪个串过来的回溯的时候 就可以判断是返回b[i][j-1]还是b[i-1][j]还是b[i-1][j-1]

      也可以不用b[][]数组 因为c[][]数组的计算规则能够帮助我们决定b c可以利用滚动数组 只记录相邻两行即可

Optimal Binary Search Tree
      BST
中从根到关键字的路径长度为代价 又知每个关键字的访问概率 求使得期望访问代价最小的
BST
     
每一棵最优BST的子树都是最优BST 然后可以知道左子树一定都小于根 一定都小于右子树 那么在一个有序序列中 确定根的位置 就可确定左右子树范围 通过递归到叶子就可解决问题

      opt[i][j]
表示有序表i~j形成的树的最小访问代价 w[i][j]表示访问概率之和 opt[i][j] = min{opt[i][k-1]+opt[k+1][j]+w[i][j]}
     
可用mid[i][j]表示取最优值时的根

      mid[i][j]的取值区间为mid[i][j-1]mid[i+1][j] 利用这个条件(四边形不等式) 可以将复杂度降为O(n^2)

Planning a Company Party
     
公司的人事结构形成树 派对中 直接上下级关系不能同时参加 每个人有个高兴值 要求列出合法名单使高兴值最大

     
opt[i][0/1]表示 以i为根的树的最大高兴值 0i参加 1i不参加
      opt[i][0] =
Σ(max{opt[k][0],opt[k][1]}) ki的孩子
      opt[i][1] =
Σ(opt[k][0])
     
结果为
max{opt[1][0], opt[1][1]}
     
方案不用存 只要从最优值出发 如果为1 则下属均不参加如果为0 判断下属的01哪个大即可

Edit Distance
     
已知一个字符串X 要变成字符串Y 操作过程将X从左至右扫描 合法操作有 1继续 2更改当前字符 3删除当前字符 4插入新字符 5交换下两个字符 6结束每个操作的代价已知 求最少代价方法

     
opt[i][j]表示字符串X[1..i]转换到Y[1..j]的最小代价 从opt[i-1][j] opt[i-1][j-1] opt[i][j-1]向后推得 利用oper[i][j]表示用了何种操作

文章来源: http://thunderfyc.wordpress.com.cn

递推
  f(x) = f(x-1)+f(x-2)
  M = ((0,1),(1,1))
  ((f(x)), (f(x+1))) * M = ((f(x+1)), (f(x+2)))
 
  ((f(x)), (f(x+1))) = ((f(1)), (f(2)))*M^(x-1)

  集合划分
    n
个元素划分到k个集合里
    f(n,k) = f(n-1, k-1) + k*f(n-1, k)
    n-1
个元素放在k-1个集合里 剩下一个单独成集合
   
或者n-1个元素在k个集合里剩下一个放置在其中任何一个

  错装信封
   
i封信不在第i个信封里
   
假设in ni上 剩下n-2个错排所以有f(n-2)*(n-1) i枚举从1n
   
假设i不在n ni上 那么去掉n n-1个位置对应n-1个数的错排(i考虑为n) 那么有f(n-1)*(n-1)

    f(n) = (f(n-1)+f(n-2))*(n-1)

数论
  gcd(a, b) = gcd(b, a mod b)

  互素的数的个数
  Phi(n) = n(1-1/p1)(1-1/p2)
(1-1/pi)

  a^b mod n
    b
的二进制表示
,b0>
   
从左到右 d = (d*d) mod n 如果bi=1 d = (d*a) mod n

高精度
排列组合
 
求排列为第几位
   
从第一个位置到最后一个 求比当前位置的数更小的数且不和前面相同的可能数 累加
   
例如 13245 0*4! + 1*3! + 0*2! + 0*1! + 0 = 6  (12345 12354 12435 12453 12534 12543 13245)
         41352 3*4! + 0*3! + 1*2! + 1*1! = 75

  求某位排列
   
从第一位开始确定 用x!的倍数来确定是在前面未出现的数中排列第几的数

  求后继
   
找到一个一直延伸到末尾的递减序列 然后将前一个值和递减数列中恰大于它的值互换后 将递减序列排序为递增
解方程
  Newton
迭代
   
求导之后 作切线 得到和x轴交点作为新的x 求导 作切线 不断逼近

计算函数解析式
  Lagrange
插值公式
   
已知n个点坐标 求过这些点的方程
    f(x) = sigma(f(ai) Pi(x-aj[j<>i])/Pi(ai-aj[j<>i]))

不定方程
  Euclid
  gcd(a, b) = ax + by
有解
  (d
,x,y) = ext_gcd(b, a mod b)  (d,x,y) = (d, y, x-[a/b]y)

  Pythagoras
  a^2+b^2=c^2
 
假设
x = a/c;  y = b/c;
     x^2+y^2=1
     y^2=(1-x)(1+x)=> y/(1+x) = (1-x)/y
 
t = y/(1+x)
   tx-y=-t;  x+ty=1;
 
t = u/v   [t为有理数
]
   a/c = (v^2 - u^2)/(u^2+v^2)
   b/c = (2uv)/(u^2+v^2)
   u,v
互素 且有一个奇数 v>u, a = v^2 - u^2; b = 2uv c = u^2 + v^2

  Pell
  x^2-ay^2 = 1
  xn = ((x1+d^0.5*y1)^n+(x1-d^0.5*y1)^n)/2
  yn = ((x1+d^0.5*y1)^n-(x1-d^0.5*y1)^n)/2d^0.5
  xn = x1xn-1 + dy1yn-1
  yn = y1xn-1 + x1yn-1

文章来源: http://thunderfyc.wordpress.com.cn

=================================================================================

猫的帽子里总有n个小猫,小猫又有小小猫,小猫的高度是猫的1/(n+1)
最小的猫高为1,已知最大的猫高度和最小的猫数,求除了最小猫的数量和所有猫的高度和

H0 = (N+1)^dep;
workNum = N^dep;
ln(H0) = dep*ln(N+1); ln(workNum) = dep*lnN;
ln(H0)/ln(workNum) = ln(N+1)/ln(N); [
此方程y -> 1 精度会有问题]

ln(H0)/ln(H0/workNum) = ln(N+1)/ln((N+1)/N) = ln(N+1) / ln(1 + 1/N)
y = ln(N+1) / ln(1+ 1/N)  [
增函数
]
Newton-Raphson 牛顿迭代法解(随便一个初始值,从函数上此点作切线,和x轴交点为新的值)

x = x0 - f(x0)/f
(x0) 迭代几次就可以了

notWorkNum = 1 + N + N^2 + + N^(dep-1) = (N^dep - 1)/(N-1) = (workNum - 1)/(N-1);
HSum = 1 + (N+1)+(N+1)^2 +
+ (N+1)^dep = ((N+1)^(dep+1) - 1)/N = (H0*(N+1) - 1)/N;

 

求数a b 满足1+2+..+a-1 = a+1++b

做方程a*(a-1) = (a+t+1+a+1)*(t+1)  x^2-2y^2 = 1
x = 2t+2a+3  y = 2a
Pell方程求解

xn = x0*xn-1 + 2*y0*yn-1
yn = x0*yn-1 + y0*xn-1

=============================================================================

Bignum Plus(a,b:Bignum)
{
  for i <- 0 to maxn
  {
    c[i] = a[i]+b[i];
    c[i+1] += c[i] div 1000;
    c[i] = c[i] mod 1000;
  }
  return c;
}

Bignum Minus(a,b:Bignum)
{
  if (less(a,b))
    return Minus(b, a);
  for i <- 0 to maxn
  {
    if (b[i] < a[i])
    {
      b[i+1]-1;
      b[i]+1000;
    }
    c[i] = b[i]-a[i];
    c[i+1] += c[i] div 1000;
    c[i] = c[i] mod 1000;
  }
  return c;
}

Bignum Multi(a,b:Bignum)
{
  for i <- 0 to maxn
  {
    c = 0;
    for j <- 0 to maxn
    {
      c[i+j] = a[i]*b[j];
      c[i+j+1] += c[i+j]  div 1000;
      c[i+j] = c[i+j] mod 1000;
    }
    d = Plus(d, c);
  }
  return d;
}

文章来源: http://thunderfyc.wordpress.com.cn

 

你可能感兴趣的:(数据结构与算法,数据结构,算法,string,扩展,c,div)