• 算法模板,自制


    目录:
    一:KMP匹配算法
    二:最小生成树算法(Prime以及Kruskal算法)
    三:最长公共子序列
    四:最长上升子序列
    五:字典树
    六:扩展欧几里德算法
    七:线段树(带有区间更新和懒惰标记)
    八:SPFA最短路径算法
    九:欧拉回路和欧拉通路的判定
    十:快速幂取模
     
    一.KMP匹配算法:
    void Kmp_Pre(int num[], int len, int kmp_pre[]) {
                   int i = 0, j = 0;
                   j = kmp_pre[0] = -1;
                   while (i < len) {
                                   while (j != -1 && num[i] != num[j]) {
                                                  j = kmp_pre[j];
                                   }
                                   kmp_pre[++i] = ++j;
                   }}
    int Kmp(int num[], int len1, int src[], int len2, int kmp_pre[]) {
                   int i = 0, j = 0;
                   Kmp_Pre(num, len1, kmp_pre);
                   while (i < len2) {
                                   while (j != -1 && num[j] != src[i]) {
                                                  j = kmp_pre[j];
                                   }
                                   i++;
                                   j++;
                                   if (j == len1) {
                                                  return i - j + 1;
                                   }
                   }
                   return -1;
    }

     

    二.最小生成树算法(Prime以及Kruskal算法):

    int cost[MAXN][MAXN];struct Edge{
                   int s, t, d; // s为起点,t为终点,d为权值} edge[MAXM];
    /*** prim算法* @param cost: 赋权邻接矩阵* @param n: 节点的编号,为1-n*/int prim(int cost[][MAXN], int n){
                   int rst = 0;
                   bool vis[MAXN]; // 标记节点是否被加入
                   int label[MAXN]; // 选择节点的依据
                   memset(vis, false, sizeof(vis));
     
                   vis[1] = true; // 从任意一个节点开始
                   for (int i = 1; i <= n; ++i) {
                                   label[i] = cost[1][i];
                   }
                   for (int times = 1; times <= n - 1; ++times) { // 循环n - 1次,每次加入一个节点
                                   int minEdge = INF;
                                   int choosed = -1;
                                   for (int i = 1; i <= n; ++i) { //选择可扩张边上的节点
                                                  if (!vis[i] && label[i] < minEdge) {
                                                                 minEdge = label[i];
                                                                 choosed = i;
                                                  }
                                   }
                                   if (minEdge == INF) { // mincost没更新,说明原图没有联通
                                                  return -1;
                                   }
                                   rst += minEdge;
                                   vis[choosed] = true;
                                   for (int i = 1; i <= n; ++i) { // 更新节点的标记值
                                                  if (!vis[i] && cost[choosed][i] < label[i]) {
                                                                 label[i] = cost[choosed][i];
                                                  }
                                   }
                   }
     
                   return rst;
    }
     
    void addEdge(int id, int s, int t, int d){
                   edge[id].s = s;
                   edge[id].t = t;
                   edge[id].d = d;}
    bool cmp(Edge edge1, Edge edge2){
                   return edge1.d < edge2.d;}
    int unionFind(int pre[], int x) {
                   int r = x;
                   while (pre[r] != r) {
                                   r = pre[r];
                   }
                   return r;}
    /*** Kruskal算法* @param*/int kruskal(Edge edge[], int n, int m){
                   int rst = 0;
                   int pre[MAXN]; // 并查集
                   for (int i = 1; i <= n; ++i) {
                                   pre[i] = i;
                   }
     
                   sort(edge, edge + m, cmp); // 对所有边按权值从小到大排序
     
                   int cnt = 0; // 对加入的边计数
                   for (int i = 0; i < m; ++i) {
                                   int r1 = unionFind(pre, edge[i].s);
                                   int r2 = unionFind(pre, edge[i].t);
                                   if (r1 != r2) { //不构成环, 就加入生成树
                                                  pre[r1] = r2;
                                                  rst += edge[i].d;
                                                  cnt++;
                                   }
                                   if (cnt >= n - 1) { // 已经加入n - 1条边,可以结束了
                                                  break;
                                   }
                   }
                   if (cnt < n - 1) { // 加入的边小于n - 1,说明原图不联通
                                   return -1;
                   }
                   return rst;
    }

     

    三.最长公共子序列:

    int main() {
                   freopen("input.txt", "r", stdin);
                   std::ios::sync_with_stdio(false);
                   //freopen("output.txt", "w+", stdout);
                   int cas;
                   while (cin >> cas) {
                                   while (cas--) {
                                                  memset(dp, 0, sizeof(dp));
                                                  int n1, n2;
                                                  cin >> n1;
                                                  for (int i = 1; i <= n1; i++) {
                                                                 cin >> num1[i];
                                                  }
                                                  cin >> n2;
                                                  for (int i = 1; i <= n2; ++i) {
                                                                 cin >> num2[i];
                                                  }
                                                  int Max = 0;
                                                  for (int i = 1; i <= n1; i++) {
                                                                 Max = 0;
                                                                 for (int j = 1; j <= n2; j++) {
                                                                                if (num1[i] > num2[j] && Max < dp[j]) {
                                                                                                Max = dp[j];
                                                                                } 
                                                                                if (num1[i] == num2[j]) {
                                                                                                dp[j] = Max + 1;
                                                                                }
                                                                 }
                                                  }
                                                  Max = 0;
                                                  for (int i = 1; i <= n2; i++) {
                                                                 if (dp[i] > Max) {
                                                                                Max = dp[i];
                                                                 }
                                                  }
                                                  cout << Max << endl;
                                   }
                   }
                   return 0;
    }
     
    四.最长上升子序列:

    如果写成递推公式,我们可以得到 dp[i]=max(dp[j](0<=j<i))+(a[i]>a[j]?1:0) 。

    于是我们就能够得到O(n^2)的动态规划方法的实现:

    const int MAXN = 1010;int n;int a[MAXN];int dp[MAXN];
    int lis()
    {
        memset(dp, 0, sizeof(dp));
        int Max;
        for (int i = 0; i < n; ++i)
        {
            Max = 0;
            for (int j = 0; j < i; ++j)
            {
                if (a[i] > a[j])
                {
                    Max = max(Max, dp[j]);
                }
            }
            dp[i] = Max + 1;
        }
        Max = 0;
        for (int i = 0; i < n; ++i)
        {
            if (dp[i] > Max)    Max = dp[i];
        }
        return Max;
    }

    O(nlogn)的动态规划+二分方法

    在前一种方法中,我们花费了很多时间在寻找最大的dp[j]上。如果有办法让这个dp[j]变成一个递增的序列,我们就能使用二分来进行优化,从而使得复杂度下降为O(nlogn)了。幸运的是,这种方法确实是存在的。我们可以使用dp[i]来保存在前i个数中最大的那个数,很容易可以理解,这个dp[i]已经是单调不减的。接下来的处理其实有些贪心的思想,对于每一个a[i],我们都在dp数组中寻找比它大的第一个数的下标,不妨设为pos,然后用a[i]来更新dp[pos]。于是我们可以明白,len就应当是max(len, pos+1)。

     

    在这里我们使用 lower_bound函数 ,这个函数将会返回小于等于val的第一个值的指针,如果不存在就返回end指针。

    const int MAXN = 1010;int n;int a[MAXN];int dp[MAXN];
    int lis()
    {
        memset(dp, 0, sizeof(int)*n);
        int len = 1;
        dp[0] = a[0];
        for (int i = 1; i < n; ++i)
        {
            int pos = lower_bound(dp, dp + len, a[i]) - dp;
            dp[pos] = a[i];
            len = max(len, pos + 1);
        }
        return len;
    }
     
    五.字典树:
    struct Node {
        int End;//代表以此结为的字符串有多少个
        int son[26];
        int deep;//字符串的第几个字符}trie[MAXN];
    int new_tire() {
        triecnt++;
        trie[triecnt].End = 0;
        for (int i = 0; i < 26; i++) {
            trie[triecnt].son[i] = 0;
        }
        return triecnt;}void init_trie() {
        triecnt = 0;
        root = new_tire();}void insert_str(char str[]) {
        int len = strlen(str);
        int rt = root;
        for (int i = len - 1; i >= 0; i--) {//题目要求是求后缀相关就如此存储,反之则正向储存
            int id = str[i] - 'a';
            if (trie[rt].son[id] == 0) {
                trie[rt].son[id] = new_tire();
                rt = trie[rt].son[id];
                trie[rt].End++;
                trie[rt].deep = len - i;
            } else {
                rt = trie[rt].son[id];
                trie[rt].End++;
                trie[rt].deep = len - i;
            }
    }
    }
     
    六.扩展欧几里德算法:

    求a * x + b * y = n的整数解的一般步骤:

    ①先计算Gcd(a,b),若n不能被Gcd(a,b)整除,则方程无整数解;否则,在方程两边同时除以Gcd(a,b),

    得到新的不定方程a' * x + b' * y = n',此时Gcd(a',b')=1;

    ②利用上面所说的欧几里德算法求出方程a' * x + b' * y = 1的一组整数解x0,y0,则n' * x0, n' * y0是方程a' * x + b' * y = n'的一组整数解;

    ③根据数论中的相关定理,可得方程a' * x + b' * y = n'的所有整数解为:

    x = n' * x0 + b' * t
    y = n' * y0 - a' * t
    (t为整数)

    上面的解也就是a * x + b * y = n 的全部整数解。

    补充一个网站看到的简便方法,最小非负整数解为(n' * x0 % b' + b') % b'。

     
    Int ExGcd(Int a, Int b, Int &x, Int &y) {/*注意这个题要求解方程时同时求出最大公约数,公约数单独求会超时= =*/
        if (b == 0) {
            x = 1;
            y = 0;
            return a;
        }
        Int gcd = ExGcd(b, a % b, x, y);
        Int t = y;
        y = x - a / b * y;
        x = t;
    return gcd;
    }
    int main() {
        //freopen("input.txt", "r", stdin);
        Int x, y, m, n, L;
        while (scanf("%I64d %I64d %I64d %I64d %I64d", &x, &y, &m, &n, &L) != EOF) {
            Int a = n - m;
            Int b = L;
            Int c = x - y;
            Int x1, y1;
            Int gcd = ExGcd(a, b, x1, y1);
            if (c % gcd) {
                printf("Impossible
    ");
                continue;
            }
            a /= gcd;
            b /= gcd;
            c /= gcd;
            Int ans = (c * x1 % b + b) % b;
            printf("%I64d
    ", ans);
        }
    return 0;
    }
     
    七.线段树(带有区间更新和懒惰标记):
    const int MX = 100050;long long sum[MX<<2];long long sign[MX<<2];int T, n, Q, X, Y;long long Z;
    void PushUp(int rt) {
        sum[rt] = sum[rt<<1] + sum[rt<<1|1];}void PushDown(int rt, int len) {
        if (sign[rt]) {
            sign[rt<<1] += sign[rt];
            sign[rt<<1|1] += sign[rt];
            sum[rt<<1] += (len - (len>>1)) * sign[rt];/*为了解决奇数所带来的子节点分配不平衡问题,因此需要这样做*/
            sum[rt<<1|1] += (len>>1) * sign[rt];
            sign[rt] = 0;
    }
    }
    void Build(int l, int r, int rt) {
        sign[rt] = 0;//初始化标记
        if (l == r) {
            scanf("%lld", &sum[rt]);
            return ;
        }
        int m = (l + r)>>1;
        Build(lson);
        Build(rson);
    PushUp(rt);
    }
    void Update(int L, int R, long long z, int l, int r, int rt) {
        if (L <= l && r <= R) {
            sign[rt] += z;//记录下更新的值
            sum[rt] += z * (r - l + 1);//批量更新
            return ;
        }
    PushDown(rt, r - l + 1);//检查标记,看是否需要继续向下更新
    int m = (l + r)>>1;
    if (L <= m) Update(L, R, z, lson);
    if (R > m) Update(L, R, z, rson);
    PushUp(rt);
    }
    long long Query(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) {
             return sum[rt];
        }
        PushDown(rt, r - l + 1);//别忘了查询时也要注意标记是否存在
        int m = (l + r)>>1;
        long long ret = 0;
        if (L <= m) ret += Query(L, R, lson);
        if (R > m) ret += Query(L, R, rson);
    return ret;
    }
     
    八.SPFA最短路径算法:
    struct Node {
                   int v;
                   int w;
                   int nxt;
    } edge[MAXN];
    long long a, b;int n, m;int edgecnt;
    void Add_Edge(int u, int v) {
                   edge[edgecnt].v = v;
                   edge[edgecnt].w = a;
                   edge[edgecnt].nxt = head[u];
                   head[u] = edgecnt++;
    }
    long long Spfa() {
                   memset(vis, false, sizeof(vis));
                   memset(dis, 0x3f, sizeof(dis));//记得是初始化为无穷大= =
                   dis[1] = 0;
                   queue<int>q;
                   while (!q.empty()) q.pop();
                   vis[1] = true;
                   q.push(1);
                   while (!q.empty()) {
                                   int now = q.front();
                                   q.pop();
                                   for (int i = head[now]; i != -1; i = edge[i].nxt) {
                                                  int v = edge[i].v;
                                                  int w = edge[i].w;
                                                  if (dis[v] > dis[now] + w) {
                                                                 dis[v] = dis[now] + w;
                                                                 if (!vis[v]) {
                                                                                vis[v] = true;
                                                                                q.push(v);
                                                                 }
                                                  }
                                   }
                   }
                   return dis[n] < b ? dis[n] : b;
    }
    //下面的用于求补图的最短路径,使用到了set,而且要求补图的权值相同,权值不同的算法另算
    long long Bfs() {
                   dis[n] = INF;
                   set<int>st, ts;
                   st.clear();
                   ts.clear();
                   for (int i = 2; i <= n; i++) {
                                   st.insert(i);
                   }
                   queue<int>q;
                   while (!q.empty()) q.pop();
                   q.push(1);
                   dis[1] = 0;
                   while (!q.empty()) {
                                   int now = q.front();
                                   q.pop();
                                   for (int i = head[now]; i != -1; i = edge[i].nxt) {
                                                  int v = edge[i].v;
                                                  if (st.count(v) == 0) {
                                                                 continue;
                                                  }
                                                  st.erase(v);
                                                  ts.insert(v);
                                   }
                                   set<int>::iterator it = st.begin();
                                   for ( ; it != st.end(); it++) {
                                                  q.push(*it);
                                                  dis[*it] = dis[now] + 1;
                                   }
                                   st.swap(ts);
                                   ts.clear();
                   }
                   return dis[n] * b < a ? dis[n] * b : a;
    }
     
    九.欧拉回路和欧拉通路的判定:

    ①欧拉通路: 通过图中每条边且只通过一次,并且经过每一顶点的通路。

    ②欧拉回路: 通过图中每条边且只通过一次,并且经过每一顶点的回路(可以回到原点,就是出发的那个点)。

    无向图是否具有欧拉通路或回路的判定:

    ①欧拉通路:图连通;图中只有0个或2个度为奇数的节点;

    ②欧拉回路:图连通;图中所有节点度均为偶数;

    有向图是否具有欧拉通路或回路的判定:

    ①欧拉通路:图连通;除2个端点外其余节点入度=出度;1个端点入度比出度大1;一个端点入度比出度小1 或 所有节点入度等于出度;

    ②欧拉回路:图连通;所有节点入度等于出度。

     

    十.快速幂取模:
    int main(){
        //freopen("input.txt", "r", stdin);
        int a, b;
        while (scanf("%d%d", &a, &b), a || b) {
            int ans = 1;
            a %= 1000;
            while (b > 0) {//快速幂取模
     
                if (b % 2 == 1) {
                    ans = (ans * a) % 1000;//如果幂的次数是奇数,就优先一步乘入结果,让幂的次数变偶数
                }
                b>>=1;
                a = (a * a) % 1000;//例如让2的四次方变成4的二次方,从而减少运算次数
            }
            printf("%d
    ", ans);
        }
    return 0;
    }

     

  • 相关阅读:
    php 高并发
    mysql 基础明细
    关于高并发和秒杀系统,你知道的和不知道的一些事
    carbon
    自定义tarbar
    学习小参考
    lnmp1.4,400,500,错误
    PHPSTORM+Thinkphp3.2模板标签替换Thinkphp5.1公式
    Thinkphp5.1手册太简单,有的功能用起来不确定结果是否和预料的一样,顾整理记录
    CentOS7 最小化安装vmware-tools
  • 原文地址:https://www.cnblogs.com/steamedbun/p/9325865.html
Copyright © 2020-2023  润新知