蓝桥杯知识点

1、memset

按照字节对内存块进行初始化,注意只能填充0或-1

memset(a,-1,sizeof(a));

2、fill

可以填充任意数字

fill(arr, arr + 10, 2);

3、求解区间[x, y]之和满足为k的倍数的数量

前缀和加上排列组合

#include <iostream>
using namespace std;

int n, k, ans, a;
long long sum[100010], dp[100010];

int main() {
    cin >> n >> k;
    for(int i = 1; i <= n; ++i){
        cin >> a;
        sum[i] = (sum[i - 1] + a) % k;
        dp[sum[i]]++;
    }
    for(int i = 0; i < k; ++i)
        // 若某两个前缀和求余后相等
        // 则任选两个数,取区间(x, y]即可使得这个区间之和求余满足题目要求
        ans += dp[i] * (dp[i] - 1) / 2;
    // 最后加上区间中元素个数为1的情况
    cout << ans + dp[0];
    return 0;
}

4、find函数

函数作用:查找该元素在数组中第一次出现的位置的地址(类似于0x的地址)

模型:find(查找起点,查找终点,查找目标元素)

同样,查找区间为[查找起点,查找终点)

字符串查找时,若未找到,则返回-1;

数组查找时,若未找到,则返回数组最后一个元素

#include <bits/stdc++.h>
using namespace std;

int main() {
    // 字符串查找
    string s = "oihfoadfgh";
    cout << s.find('a') << endl;
    
    // 数组查找
    int a[10] = {2, 6, 8, 1, 3, 7, 5, 1, 0, 11};
    // 打印类似0x地址
    cout << find(a, a + 5, 8) << endl;
    // 打印数组【】内的序号
    cout << find(a, a + 5, 8) - a << endl;
    return 0;
}

5、 PI(Π)

double PI = atan(1.0) * 4;

6、数据范围

int的范围大概为:2e9

long long的范围大概为:9e18

7、读取一行字符串

getline(cin, s);

8、判断某个字符是否是字母

bool flag = isalpha(s[i]);

9、判断某个数是否是回文数

//是否回文
bool is_pali(int x) {
    string str = to_string(x);
    string s = str;
    reverse(s.begin(), s.end());
    return s == str;
}

10、卡片换位

空格也可以代表一个人物(这里将他命名为K),这个人物的特殊之处在于他可以与周围的任何一个人交换位置

那么我们在最开始的时候就记录几个特殊点的坐标:A关羽,B张飞,K空格,就能够设置搜索的边界了

那么进行搜索时总要有一个开始点,不妨我们选择让空格主动出击,主动去和周围的人物交换,进行上下左右的尝试

每dfs一次就是对华容道阵容的一次更新

#include <bits/stdc++.h>
using namespace std;

// 设置成最大值
int Min = INT_MAX;

// 结构体定义A,B,空格坐标
struct
{
    int x, y;
}a, b, k;

//记忆化搜索
int vis[3][3][3][3][3][3];
//上下左右走
int Next[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};

void dfs(int x1, int y1, int x2, int y2, int x, int y, int step) {
    //边界1,走不出去了,所走的步数已经比求得的最小值大,再走下去也没意义
    if (step > Min)
        return;
    //边界2,交换完成
    if (x1 == b.x && y1 == b.y && x2 == a.x && y2 == a.y) {
        Min = min(step, Min);
        return;
    }
    //边界3,走出迷宫边界
    if (x < 0 || x > 1 || y < 0 || y > 2)
        return;
    // 边界4,已经搜索过这种情况了
    if (vis[x1][y1][x2][y2][x][y] == 1)
        return;
    vis[x1][y1][x2][y2][x][y] = 1;

    // 开始dfs主干部分
    // 四个方向搜索
    for (int i = 0; i < 4; i++) {
        int tx = x + Next[i][0];
        int ty = y + Next[i][1];
        if (tx == x1 && ty == y1)//空格与A交换位置
            dfs(x, y, x2, y2, x1, y1, step + 1);
        else if (tx == x2 && ty == y2)//空格与B交换位置
            dfs(x1, y1, x, y, x2, y2, step + 1);
        else//空格与*交换位置
            dfs(x1, y1, x2, y2, tx, ty, step + 1);
    }
    vis[x1][y1][x2][y2][x][y] = 0;
}

int main() {
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            char s = getchar();
            if (s == 'A') {
                a.x = i;
                a.y = j;
            }
            if (s == 'B') {
                b.x = i;
                b.y = j;
            }
            if (s == ' ') {
                k.x = i;
                k.y = j;
            }
        }
        getchar();
    }
    dfs(a.x, a.y, b.x, b.y, k.x, k.y, 0);
    printf("%d", Min);
    return 0;
}

11、人物相关性分析

 将每个Alice、Bob的位置找出来,然后在需要的范围内进行遍历来查找两者同时出现的次数

#include <iostream>
#include <vector>
using namespace std;

int k;
string s;
long long ans;

int main() {
    cin >> k >> s;
    vector<int> Alice, Bob;
    for (int i = 0; i < s.size();) {
        if (s.substr(i, 5) == "Alice") {
            if (i - 1 >= 0 && isalpha(s[i - 1]) || i + 5 < s.size() && isalpha(s[i + 5]))
                continue;
            Alice.push_back(i);
            i += 5;
        }
        else if (s.substr(i, 3) == "Bob") {
            if (i - 1 >= 0 && isalpha(s[i - 1]) || i + 3 < s.size() && isalpha(s[i + 3]))
                continue;
            Bob.push_back(i);
            i += 3;
        }
        else i++;
    }

    for (int i = 0, l = 0, r = 0; i < Alice.size(); i++) {
        //维护窗口左边界
        while (l < Bob.size() && Bob[l] < Alice[i] - k - 3)
            l++;
        //维护窗口右边界
        while (r < Bob.size() && Bob[r] < Alice[i] + k + 5)
            r++;
        ans += r - l;
    }
    cout << ans;
    return 0;
}

12、文件

freopen("D:\\desktop\\input.txt","r",stdin);

13、设置输出宽度和前缀

// 前面有25-3=22个空格
cout << setw(25) << 256 << endl;
// 前面有7个9
cout << setfill('9') << setw(10) << 256;

14、全排列

#include <bits/stdc++.h>
using namespace std;

int a[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
            11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
bool vis[20];
int n, b[20];

void dfs(int s) {
    if (s == n) {
        for (int i = 0; i < n; i++)
            cout << b[i] << ' ';
        cout << endl;
        return;
    }
    for (int i = 0; i < n; i++)
        if (!vis[i]) {
            vis[i] = true;
            b[s] = a[i];
            dfs(s + 1);
            vis[i] = false;
        }
    return;
}

int main() {
    cin >> n;
    dfs(0);
}

15、n个数参与m层全排列

#include <bits/stdc++.h>
using namespace std;

bool vis[20];
int a[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
          12, 13, 14, 15, 16, 17, 18, 19, 20};
int b[20], step;

void dfs(int s,int step) {
    if (s == step) {
        for (int i = 0; i < step; i++)
            printf("%d ", b[i]);
        printf("\n");
        return;
    }
    for (int i = 0; i < 20; i++)
        if (!vis[i]) {
            vis[i] = true;
            b[s] = a[i];
            dfs(s + 1, step);
            vis[i] = false;
        }
}

int main() {
    scanf("%d", &step);
    dfs(0, step);
    return 0;
}

16、前n个数的所有组合(不含空集合的所有子集)

#include <bits/stdc++.h>
using namespace std;

bool vis[20];
int a[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
                          12, 13, 14, 15, 16, 17, 18, 19, 20};
int step;

void dfs(int s) {
    if (s == step) {
        for (int i = 0; i < step; i++)
            if (vis[i])
                printf("%d ", a[i]);
        printf("\n");
        return;
    }
    else {
        vis[s] = true;
        dfs(s + 1);
        vis[s] = false;
        dfs(s + 1);
    }
}

int main() {
    scanf("%d", &step);
    dfs(0);
    return 0;
}

17、最大公约数、最小公倍数

// 最大公约数
cout << __gcd(25, 5) << endl;
// 最小公倍数lcm
cout << 25 * 5 / __gcd(25, 5) << endl;

18、十进制转换为任意进制

itoa函数,格式:itoa(十进制数,字符数组名称,转换的进制数)

将十进制的n转化为m进制,并将结果存储在c数组中

#include <bits/stdc++.h>
using namespace std;

int n;
char c[25];

int main() {
    // 输入十进制、输出任意进制
    cin >> n;
    cout << itoa(n, c, m);
    return 0;
}

19、多重背包问题

 类似于0/1背包

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1e5 + 10;
int n, V, cnt, a, b, s, v[MAXN], w[MAXN], f[MAXN];

int main() {
    cin >> n >> V;
    for (int i = 1; i <= n; i++) {
        cin >> a >> b >> s;
        int k = 1;
        //实现1 2 4 8件原物品,合成为新物品
        while (k <= s) {
            v[++cnt] = k * a;
            w[cnt] = k * b;
            s -= k;
            k *= 2;
        }
        if (s) {
            v[++cnt] = s * a;
            w[cnt] = s * b;
        }
    }

    //01背包
    for (int i = 1; i <= cnt; i++)
        for (int j = V; j >= v[i]; j--)
            f[j] = max(f[j], f[j - v[i]] + w[i]);
    cout << f[V];
    return 0;
}

20、分组背包问题

for (int i = 1; i <= n; i++)
    for (int j = m; j >= 0; j++)
        //s[i]表示第i组物品的个数
        for (int k = 1; k <= s[i]; k++)
            //剩余的背包容量j大于第i组的第k个物品的体积
            if(j >= v[i][k])
                f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);

21、包子凑数

#include <bits/stdc++.h>
using namespace std;

int N, ans = 0, a[101], dp[10001];

int main() {
    cin >> N >> a[0];
    int g = a[0];
    for (int i = 1; i < N; i++) {
        cin >> a[i];
        g = __gcd(g, a[i]);
    }
    if (g != 1) {
        cout << "INF";
        return 0;
    }
    fill(dp, dp + 10000, 10000);
    dp[0] = 0;
    // 背包问题
    for (int i = 1; i < 10001; i++)
        for (int j = 0; j < N; j++)
            if (i - a[j] >= 0)
                dp[i] = min(dp[i], dp[i - a[j]] + 1);
    for (int i = 1; i < 10001; i++)
        if (dp[i] >= 10000)
            ans++;
    cout << ans;
    return 0;
}

22、超级质数

 求不了子数字,但能求子字符串

#include <bits/stdc++.h>
using namespace std;

int n, cnt, ans, son_int;
string son_str;

bool is_prime(int target) {
    for (int i = 2; i <= sqrt(target); ++i)
        if (target % i == 0)
            return false;
    return true;
}

int main() {
    for (int i = 2; i < 1010; i++) {
        //字符串化
        string str = to_string(i);
        cnt = 0;
        n = str.length();
        // 遍历所有长度
        for(int j = 1; j <= n; ++j)
            for(int k = 0; k <= n - j; ++k) {
                son_str = str.substr(k, j);
                son_int = stoi(son_str);
                if(!is_prime(son_int))
                    cnt++;
            }
        if (cnt == 0)
            ans = son_int;
    }
    cout << ans;
    return 0;
}

23、冒泡排序

循环n次 

for (int i = 0; i < n; i++)
    for (int j = 1; j < n; j++)
        if (a[j] < a[j - 1])
            swap(a[j], a[j - 1]);

 24、最小生成树

prim算法:

首先从某个点出发,找到与之相连的最小权值的边,并将其回贴到图中,然后在此时的最小生成树中,找到某点与(还未加入最小生成树中的某点)形成的最小权值的边,并将其回贴到图中,如此递归形成最后的结果,详情见代码

#include<bits/stdc++.h>
using namespace std;
const int INF = 1e9 + 10;
const int maxn = 5010;
const int maxm = 2e5 + 10;
 
struct edge{
    int v, w, next;
}e[maxm * 2];
//cnt表示有向边的条数
//tot表示已连接的最小生成树的边
//now代表此时准备连接哪个点
//ans表示结果
int cnt, n, m, tot, now = 1, ans;
//head表示某点作为前驱节点时的编号
//dis表示已经加入最小生成树的点到没有加入的点的最小距离
//vis判断某个点是否已经加入最小生成树
int head[maxn], dis[maxn], vis[maxn];
 
void add(int u, int v, int w) {
    e[++cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt;
}
 
void prim() {
    for(int i = 2; i <= n; ++i)
        dis[i] = INF;
    //注意重边
    //找出所有与1相连的点,记录两点间的最短距离
    for(int i = head[1]; i; i = e[i].next)
        dis[e[i].v] = min(dis[e[i].v], e[i].w);
    //最小生成树边数等于点数 - 1
    while(++tot < n) {
        int minn = INF;
        //标记走过的点
        vis[now] = 1;
        int t = now;
        //枚举每一个没有使用的点
        //找出最小值作为新边
        for(int i = 1; i <= n; ++i)
            if(!vis[i] && minn > dis[i]) {
                minn = dis[i];
                now = i;
            }
        //如果最新的结点未被修改,则无法形成最小生成树
        //故该图不连通,则需输出orz
        if(now == t) {
            printf("orz");
            return;
        }
        ans += minn;
        //枚举now的所有连边,更新dis数组
        for(int i = head[now]; i; i = e[i].next) {
            int v = e[i].v;
            if(dis[v] > e[i].w && !vis[v])
                dis[v] = e[i].w;
        }
    }
    printf("%d", ans);
}
 
int main() {
    scanf("%d %d", &n, &m);
    int u, v, w;
    for(int i = 1; i <= m; ++i) {
        scanf("%d %d %d", &u, &v, &w);
        //该图为无向图
        //故需要当作两条边加入结构体中
        add(u, v, w);
        add(v, u, w);
    }
    prim();
    return 0;
}
Kruskal算法:

将所有的边按权值的大小从小到大进行排序,选取权值最小的边,回贴到图中并判断是否形成了环,若形成了环,则丢弃该边,继续下一条边的回贴,若没有形成环,则递归调用,继续下条边的判断。此时的判断有没有形成环,可以用有没有公共祖先进行判断,若存在公共祖先,则会形成环,否则不会,详情见代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 10;
const int maxv = 2e5 + 10;
int n, m, ans, f[maxv];
//因为该图为无向图,故存储时需要将数组长度扩大一倍
struct edge{
    int u, v, w;
}g[maxn];
 
bool cmp(edge a,edge b) {
    return a.w < b.w;
}
 
int find(int a) {
    if (f[a] == a)
        return a;
    return f[a] = find(f[a]);
}
 
void construct() {
    int cnt = 0;
    for (int i = 1; i <= n; i++)
        f[i] = i;
    for (int i = 1; i <= m; i++) {
        int fx = find(g[i].u);
        int fy = find(g[i].v);
        //该步骤是为了判断是否形成了环
        if (fy != fx) {
            f[fx] = fy;
            ans += g[i].w;
            cnt++;
        }
        if (cnt == n - 1) {
            printf("%d", ans);
            break;
        }
    }
    if(cnt != n - 1)
        printf("orz");
}
 
int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= m; i++)
        scanf("%d %d %d", &g[i].u, &g[i].v, &g[i].w);
    sort(g + 1, g + 1 + m, cmp);
    construct();
    return 0;
}

25、链式前向星

初始化定义结构体
// 定义结构体
struct E {
    int to, w, next;
}Edge[maxm];
int tot, Head[maxm];
插入节点,从头部插入
// 插入节点
void AddEdge(int u,int v,int w) {
    Edge[tot].to = v;
    Edge[tot].w = w;
    Edge[tot].next = Head[u];
    Head[u] = tot++;
}
遍历
for(int i = Head[u]; ~i; i = Edge[i].next) {
    int v = Edge[i].to;
    int w = Edge[i].w;
}

26、取模符号

取模运算的符号取决于被除数

printf("%d\n", 7 % (-3));//1
printf("%d\n", (-7) % 3);//-1

27、二叉树的遍历(前序、中序、后序)

#include <bits/stdc++.h>
using namespace std;

struct Node {
    int data;
    Node *left, *right;
}root[1050];

int n, l, r;

//前序遍历
void PreOrderTree(Node* root) {
    if (root == NULL)
        return;
    printf("%d ", root->data);
    PreOrderTree(root->left);
    PreOrderTree(root->right);
}

//中序遍历
void MidOrderTree(Node* root) {
    if (root == NULL)
        return;
    MidOrderTree(root->left);
    printf("%d ", root->data);
    MidOrderTree(root->right);
}

//后序遍历
void PostOrderTree(Node* root) {
    if (root == NULL)
        return;
    PostOrderTree(root->left);
    PostOrderTree(root->right);
    printf("%d ", root->data);
}

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d %d", &l, &r);
        root[i].data = i;
        //若l不为零,则将该结点作为左孩子
        if(l)
            root[i].left = &root[l];
        //若r不为零,则将该结点作为右孩子
        if(r)
            root[i].right = &root[r];
    }
    //从根节点开始遍历
    PreOrderTree(&root[1]);
    printf("\n");
    MidOrderTree(&root[1]);
    printf("\n");
    PostOrderTree(&root[1]);
    return 0;
}

28、快速幂

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll base, power;

ll fastPower(ll base,ll power) {
    ll result = 1;
    while (power > 0) {
        if (power % 2 == 1)
            result *= base;
        power /= 2;
        base *= base;
    }
    return result;
}

int main() {
    // 求base的power次方
    cin >> base >> power;
    cout << fastPower(base, power);
    return 0;
}

29、线性筛素数

#include<bits/stdc++.h>
using namespace std;
const int N = 1e8 + 10;
int n, q, k, cnt, isPrime[N], Prime[N];

void GetPrime() {
    // 初始化,令每个数均为素数
    memset(isPrime, 1, sizeof(isPrime));
    isPrime[1] = 0;
    for(int i = 2; i <= n; ++i) {
        // 若这个数没有被筛掉,则记为素数
        if(isPrime[i])
            Prime[++cnt] = i;
        // 不管i是不是素数,需要筛掉i和所有已知的素数的积,但是每个积只需要筛一次
        // 由这个积除以最小质因数的商来筛掉
        for(int j = 1; j <= cnt && i * Prime[j] <= n; ++j) {
            // 若有某个数可以整除i和已经筛选出来的素数,则标记
            isPrime[i * Prime[j]] = 0;
            // 如果当前的i有Prime[j]这个质因数,假设i = k * Prime[j]
            // 后续就没有必要使用i和更大的质因数继续筛选下去了
            // eg.数i * Prime[j + 1] = k * Prime[j] * Prime[j + 1]
            // 此时应该使用k * Prime[j + 1]进行筛选(除以最小质因数的商)
            // 减少时间复杂度
            if(i % Prime[j] == 0)
                break;
        }
    }
}

int main() {
    scanf("%d %d", &n, &q);
    GetPrime();
    while(q--) {
        scanf("%d", &k);
        printf("%d\n", Prime[k]);
    }
    return 0;
}

30、汉诺塔

#include <bits/stdc++.h>
using namespace std;

void move(char A, char C, int n) {
    cout << "把第" << n << "个圆盘从" << A << "--->" << C << endl;
}

void HanoiTower(char A, char B, char C, int n) {
    if (n == 1)
        move(A, C, n);
    else {
        //将n-1个圆盘从A柱借助于C柱移动到B柱上
        HanoiTower(A, C, B, n - 1);
        //将A柱子最后一个圆盘移动到C柱上
        move(A, C, n);
        //将n-1个圆盘从B柱借助于A柱移动到C柱上
        HanoiTower(B, A, C, n - 1);
    }
}

int main() {
    int n = 0;
    cout << "输入A柱子上的圆盘个数:";
    cin >> n;
    //将n个圆盘从A柱借助于B柱移动到C柱上
    HanoiTower('A', 'B', 'C', n);
    return 0;
}

31、vector

#include <bits/stdc++.h>
using namespace std;

vector<int> v;

int main() {
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    // insert(起始地址,插入个数,插入字符)
    v.insert(v.begin(), 5, 9);
    // 遍历vector
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
        cout << *it << endl;
    return 0;
}

32、字符串string

#include <bits/stdc++.h>
using namespace std;

int main() {
    string s = "abcdefgbc";
    // 正序查找
    cout << s.find("bc") << endl;
    // 倒序查找
    cout << s.rfind("bc") << endl;
    //insert(插入位置,插入字符)
    s.insert(0, "123");
    cout << s << endl;  // 123abcdefgbc
    //erase(擦除位置,擦除长度)
    s.erase(1, 3);  // 1bcdefgbc
    cout << s << endl;
    //replace(起始位置,替代长度,替代字符串)
    s.replace(1, 3, "xyz"); // 1xyzefgbc
    cout << s << endl;
    // toupper()函数,字符串转大写
    for( int i = 0; i < s.size(); i++ ) // 1XYZEFGBC
        s[i] = toupper(s[i]);
    cout << s << endl;
    // tolower()函数,字符串转小写
    for( int i = 0; i < s.size(); i++ ) // 1xyzefgbc
        s[i] = tolower(s[i]);
    cout << s << endl;
    // str.append("***");在字符串str后面添加字符串
    // compare()函数:比较字符串 s1.compare(s2)
    cout << s.compare("abc") << endl;
    //其他查找
    vector<int> v;
    v.push_back(123456789);
    if (find(v.begin(), v.end(), 123456789) != v.end())
        cout << "win";
    return 0;
}

33、优先队列

#include <bits/stdc++.h>
using namespace std;

//升序队列  小顶堆 great 小到大
priority_queue <int, vector<int>, greater<int> > pri_que1;
//降序队列  大顶堆 less  大到小 默认
priority_queue <int, vector<int>, less<int> > pri_que2;

int main() {
    // 插入元素
    pri_que1.push(1);
    pri_que1.push(5);
    pri_que1.push(2);
    pri_que1.push(8);
    pri_que1.push(3);
    // 判断是否为空
    while(!pri_que1.empty()) {
        // 输出第一个元素
        cout << pri_que1.top() << ' ';
        // 删掉第一个元素
        pri_que1.pop();
    }
    cout << endl;
    pri_que2.push(1);
    pri_que2.push(5);
    pri_que2.push(2);
    pri_que2.push(8);
    pri_que2.push(3);
    while(!pri_que2.empty()) {
        cout << pri_que2.top() << ' ';
        pri_que2.pop();
    }
    return 0;
}

34、尺取法

题目描述:给定一个长度为n的数组a和一个数s,在这个数组中找一个区间,使得这个区间之和等于s。输出区间的起点和终点位置。
思路:
1.初始值i=0、j=0,即开始都指向第一个元素a[0],定义sum是区间[i,j]的和,初始值sum = a[0]。
2.如果sum等于s,输出一个解。继续,把j往后挪一位,并把sum的值加上这个新元素
3.如果sum大于s,让sum减掉元素a[i],并把i往后移动一位。
4.如果sum小于s,把j往后挪一位,并把sum的值加上这个新元素。

#include <bits/stdc++.h>
using namespace std;

int a[9] = {1, 2, 3, 4, 10, 4, 3, 2, 1};
int i = 0, j = 0, sum = a[0], target = 10;

int main() {
    while (i <= j && i <= 8 && j <= 8) {
        // 等于情况下输出后把j挪后一位,注意不要挪i,否则可能会导致程序提前终止
        if (sum == target) {
            cout << i << ' ' << j << endl;
            j++;
            sum = sum + a[j];
            continue;
        }
        // 大于情况下减去i对应值
        if (sum > target) {
            sum = sum - a[i];
            i++;
            continue;
        }
        // 小于情况下加上j对应值
        if (sum < target) {
            j++;
            sum = sum + a[j];
            continue;
        }
    }
    return 0;
}

35、KMP算法

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 5;
int Next[maxn], n1, n2;
string str, mo;

void GetNext() {
    int i = 0, j = -1;
    while (i < n2) {
        if (j == -1 || mo[i] == mo[j]) {
            i++;
            j++;
            Next[i] = j;
        }
        else
            j = Next[j];
    }
}

void kmp() {
    int cnt = 0;
    int i = 0, j = 0;
    while (i < n1) {
        if (j == -1 || str[i] == mo[j]) {
            i++;
            j++;
        }
        else
            j = Next[j];
        if (j == n2) {
            // 计算次数
            cnt++;
            // 返回位置
            cout << i - n2 + 1 << endl;
            j = Next[j];
        }
    }
    cout << cnt;
}

int main() {
    cin >> str >> mo;
    n1 = str.length();
    n2 = mo.length();
    Next[0] = -1;
    GetNext();
    kmp();
    return 0;
}

36、LIS算法(最长上升子序列)+ 二分

#include <bits/stdc++.h>
using namespace std;

// a数组为数据,dp[i]表示长度为i + 1的LIS结尾元素的最小值
int a[99999], dp[99999];

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        // 初始化为无限大
        dp[i] = INT_MAX;
    }
    // 记录dp当前最后一位的下标
    int pos = 0;
    dp[0] = a[0];
    for (int i = 1; i < n; i++) {
        // 若a[i]大于dp数组最大值,则直接添加
        if (a[i] > dp[pos])
            dp[++pos] = a[i];
            // 否则找到dp中第一个大于等于a[i]的位置,用a[i]替换之
        else
            // 二分查找,在从小到大的排序数组中
            // lower_bound( begin, end, num):从数组的[begin,end)二分查找第一个大于或等于num的数字
            // 找到返回该数字的地址,不存在则返回end
            // 通过返回的地址减去起始地址begin, 得到找到数字在数组中的下标
            // upper_bound( begin, end, num):从数组的[begin,end)二分查找第一个大于num的数字
            // 找到返回该数字的地址,不存在则返回end
            // 通过返回的地址减去起始地址begin, 得到找到数字在数组中的下标
            dp[lower_bound(dp, dp + pos + 1, a[i]) - dp] = a[i];
    }
    cout << pos + 1;
    return 0;
}

37、LCS算法(最长公共子序列)

#include<bits/stdc++.h>
using namespace std;

int DP[1000][1000], n, m;

//长度
int LCS_length(string a, string b) {
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++) {
            if (a[i - 1] == b[j - 1])
                DP[i][j] = DP[i - 1][j - 1] + 1;
            else if (DP[i - 1][j] >= DP[i][j - 1])
                DP[i][j] = DP[i - 1][j];
            else
                DP[i][j] = DP[i][j - 1];
        }
    return DP[m][n];
}

//具体公共字符串
void LCS(string a, string b, int i, int j) {
    //设置边界
    if (i == 0 || j == 0)
        return;
    if (a[i - 1] == b[j - 1]) {
        LCS(a, b, i - 1, j - 1);
        cout << a[i - 1];
    }
    else if (DP[i - 1][j] > DP[i][j - 1])
        LCS(a, b, i - 1, j);
    else
        LCS(a, b, i, j - 1);
}

int main() {
    string a, b;
    cin >> a >> b;
    m = a.size();
    n = b.size();
    cout << LCS_length(a, b) << endl;
    LCS(a, b, m, n);
    return 0;
}

38、树状数组

单点修改、区间求和

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int n, m, a, flag, p, q, tree[N];

//非负整数n在二进制表示下最低位1及其后面的0构成的数值
//eg.lowbit(12) = lowbit((1100)2) = (100)2 = 4
//将1100按位取反后加一得到0100,会发现除了最低位的一和后面的零,其余位上与原数均相反
//故两者按位与后正好得到最低位1及其后面的0构成的数值
//又取反加一为补码,故lowbit为k & -k
int lowbit(int k) {
    return k & -k;
}

//tree[x]保存以x为根的子树中叶节点值的和
//将x转化为二进制后,发现每一层的末尾的零的个数都相同
//且tree[x]覆盖的长度即为lowbit(x)的值
//tree[x]的父节点为tree[x + lowbit(x)]
void add(int x, int k) {
    while(x <= n) {
        tree[x] += k;
        x += lowbit(x);
    }
}

//可知,若求前7项的和,则该值为tree[7] + tree[6] + tree[4]
//故,通过循环可以求出结果
int sum(int x) {
    int ans = 0;
    while(x != 0) {
        ans += tree[x];
        x -= lowbit(x);
    }
    return ans;
}

int main() {
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a);
        add(i, a);
    }
    for(int i = 1; i <= m; ++i) {
        scanf("%d %d %d", &flag, &p, &q);
        if(flag == 1)
            add(p, q);
        else
            printf("%d\n", sum(q) - sum(p - 1));
    }
    return 0;
}
区间修改,单点查询:

重点在差分数组上 

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int n, m, now, last, flag, p, q, num, tree[N];

int lowbit(int k) {
    return k & -k;
}

void add(int x, int k) {
    while(x <= n) {
        tree[x] += k;
        x += lowbit(x);
    }
}

int query(int x) {
    int ans = 0;
    while(x != 0) {
        ans += tree[x];
        x -= lowbit(x);
    }
    return ans;
}

int main() {
    scanf("%d %d", &n, &m);
    //计算差分数组,将相差的值放入数组中
    //eg.原本的数组应为a[] = {1, 6, 8, 5, 10}
    //则差分数组应为b[] = {1, 5, 2, -3, 5}
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &now);
        add(i, now - last);
        last = now;
    }
    for (int i = 1; i <= m; ++i) {
        scanf("%d", &flag);
        //若要修改区间[p, q]的值
        //例如上述举的例子,若要将区间[2, 4]均加上3
        //则原数组变为a[] = {1, 9, 11, 8, 10}
        //差分数组变为b[] = {1, 8, 2, -3, 2}
        //即对差分数组来说只需修改下标为p的值,和下标为q + 1的值
        if (flag == 1) {
            scanf("%d %d %d", &p, &q, &num);
            add(p, num);
            add(q + 1, -num);
        }
        //若查询某个点的值
        //前p个差分数组的值相加即为该点的值
        //与单点修改、区间查询中的求前缀和类似
        else {
            scanf("%d", &p);
            printf("%d\n", query(p));
        }
    }
    return 0;
}

39、景区导游(LCA、图论)

样例输入:
6 4
1 2 1
1 3 1
3 4 2
3 5 2
4 6 3
2 6 5 1

样例输出:
4

 本题主要考察LCA

若要求u->v的路径长度,等于求u->root+v->root - 2*(root->LCA(u,v))

即分别从u和v走到根结点,再减去2倍的根结点到(u,v)的最近公共祖先的路径长度

同理,若原本的遍历顺序是a->b->c,总路径长度为sum,若要跳过b

则新的路径长度为sum-dist(a,b)-dist(b,c)+dist(a,c),可画图验证

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e5 + 100;

int n, k;
int deep[N];//深度
int dp[N][21];//dp[i][j]表示从i结点开始跳2^j步可到达的结点
vector<int> edge[N];//边
vector<int> weight[N];//权值
ll path[N];//原始的游览路线
ll dist[N];//dist[i]存储i到根结点的距离


//LCA的前置算法(模板)
void dfs(int u,int father) {
    deep[u] = deep[father] + 1;
    dp[u][0] = father;
    for (int i = 1; i <= 20; i++)
        dp[u][i] = dp[dp[u][i - 1]][i - 1];

    for (int i = 0; i < edge[u].size(); i++) {
        int v = edge[u][i], w = weight[u][i];
        if (v == father)
            continue;
        dist[v] = dist[u] + w;
        dfs(v, u);
    }
}

//求x和y的最近公共祖先(模板)
int LCA(int x, int y) {
    if (deep[x] < deep[y])
        swap(x, y);
    for (int i = 20; i >= 0; i--)
        if (deep[dp[x][i]] >= deep[y])
            x = dp[x][i];
    if (x == y)
        return x;
    for (int i = 20; i >= 0; i--)
        if (dp[x][i] != dp[y][i]) {
            x = dp[x][i];
            y = dp[y][i];
        }
    return dp[x][0];
}

//求x和y的距离
ll get_dist(int x,int y) {
    if (x == 0 || y == 0)
        return 0;
    return dist[x] + dist[y] - 2 * dist[LCA(x, y)];
}

int main() {
    scanf("%d%d", &n, &k);
    //插入n-1条无向边
    for (int i = 1; i < n; i++) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        edge[u].push_back(v);
        edge[v].push_back(u);
        weight[u].push_back(w);
        weight[v].push_back(w);
    }
    //跑一遍dfs为LCA做准备
    dfs(1, 0);
    // sum存储原始游览路线的总路径长度
    ll sum = 0;
    for (int i = 1; i <= k; i++) {
        scanf("%d", &path[i]);
        //依次累加
        sum += get_dist(path[i], path[i - 1]);
    }
    //除去第i个景点
    for (int i = 1; i <= k; i++) {
        ll dist1 = get_dist(path[i], path[i - 1]);
        ll dist2 = get_dist(path[i], path[i + 1]);
        ll dist3 = get_dist(path[i - 1], path[i + 1]);
        printf("%lld ", sum - dist1 - dist2 + dist3);
    }
    printf("\n");
    return 0;
}

40、砍树(LCA、图论)

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
typedef long long ll;

const int maxn = 1e5 + 50;

vector<int> e[maxn];
ll fa[maxn], dep[maxn], son[maxn], siz[maxn], top[maxn];
ll diff[maxn];

void dfs1(ll u, ll f) {
    fa[u] = f;
    dep[u] = dep[f] + 1;
    siz[u] = 1;
    for (auto x: e[u]) {
        ll v = x;
        if (v == f)
            continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if (siz[v] > siz[son[u]])
            son[u] = v;
    }
}

void dfs2(ll u, ll t) {
    top[u] = t;
    if (!son[u])
        return;
    dfs2(son[u], t);
    for (auto x: e[u]) {
        ll v = x;
        if (v != son[u] && v != fa[u]) dfs2(v, v);
    }
}

ll lca(ll x, ll y) {
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]])
            swap(x, y);
        x = fa[top[x]];
    }
    return dep[x] > dep[y] ? y : x;
}

// 给路径打上标记,若标记数大于m,则说明删除后可以满足要求
void dfs(int x, int fx) {
    for (auto y: e[x]) {
        if (y == fx)
            continue;
        dfs(y, x);
        diff[x] += diff[y];
    }
}

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        e[u].emplace_back(v);
        e[v].emplace_back(u);
    }
    dfs1(1, 1);
    dfs2(1, 1);
    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        diff[u]++;
        diff[v]++;
        diff[lca(u, v)] -= 2;
    }
    dfs(1, 0);
    ll ans = -1;
    for (int i = 0; i <= n; i++)
        if (diff[i] >= m)
            ans = i - 1;
    cout << ans;
    return 0;
}

41、依次读取数据,事先并不知道有多少数据

int a[N], cnt;
while (scanf("%d", &a[cnt]) != EOF)
    cnt++;

42、STL全排列函数

#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    string s = "bca";
    //字符串内部排序,得到最小的排列"abc"
    sort(s.begin(), s.end());
    //s.end()指向最后一个字符的下一个位置
    // abc acb bac bca cab cba
    // 全排列函数,用来求后一个排列
    do {
        cout << s << " ";
    } while (next_permutation(s.begin(), s.end()));
    printf("\n");
    // 从大到小进行排序
    sort(s.begin(), s.end(), greater<>());
    // cba cab bca bac acb abc
    // 全排列函数,用来求前一个排列
    do {
        cout << s << " ";
    } while (prev_permutation(s.begin(), s.end()));
    return 0;
}

43、求数组最大/最小值

#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    int n = 8, a[] = {10, 5, 95, 65, 445, 6852, 57, 864};
    cout << *max_element(a, a + n) << endl;    // 6852
    cout << *min_element(a, a + n) << endl;    // 5
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值