算法模板,自制

目录:
一: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\n");
            continue;
        }
        a /= gcd;
        b /= gcd;
        c /= gcd;
        Int ans = (c * x1 % b + b) % b;
        printf("%I64d\n", 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\n", ans);
    }
return 0;
}

 

猜你喜欢

转载自www.cnblogs.com/steamedbun/p/9325865.html