藍橋杯備賽知識點總結

一、數論

如果想要計算整除向上取整(x+y-1)/y 或者(x-1)/y +1

最大公約數:

int gcd(int a,int b){return b==0?a:gcd(b,a%b);
}

最小公倍數:

int lcm(int a,int b){return a/gcd(a,b)*b;
}

埃氏篩法:

// C++ program to print all primes smaller than or equal to
// n using Sieve of Eratosthenes
#include <bits/stdc++.h>
using namespace std;
void SieveOfEratosthenes(int n)
{// Create a boolean array "prime[0..n]" and initialize// all entries it as true. A value in prime[i] will// finally be false if i is Not a prime, else true.vector<bool> prime(n + 1, true);for (int p = 2; p * p <= n; p++) {if (prime[p] == true) {// Update all multiples of p greater than or// equal to the square of it numbers which are// multiple of p and are less than p^2 are// already been marked.for (int i = p * p; i <= n; i += p)prime[i] = false;}}// Print all prime numbersfor (int p = 2; p <= n; p++)if (prime[p])cout << p << " ";
}// Driver Code
int main()
{int n = 30;cout << "Following are the prime numbers smaller "<< " than or equal to " << n << endl;SieveOfEratosthenes(n);return 0;
}

唯一分解定理:

#include<bits/stdc++.h>
using namespace std;
vector<pair<int,int>> nums;
int main(){int x;cin>>x;for(int i=2;i<x/i;i++){if(x%i){continue;}int cnt=0;while(x%i==0){cnt++;x/=i;}nums.push_back({i,cnt});}if(x>1){nums.push_back({x,1});}int v=nums.size();for(const auto i: nums){cout<<i.first<<"^"<<i.second<<endl;}return 0;
}

快速冪:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int qmi(ll a,ll b){ll res=1;while(b){if(b&1){res=res*a;}a=a*a;b>>=1;}return res;
}
int main(){ll x,n;cin>>x>>n;ll ans=qmi(x,n);cout<<ans;return 0;
}

費馬小定理:

當 p?是質數時,a 的 (p?1)次方除以 p?的余數必為1 ?

逆元:

ll inv(ll x){return qmi(x,p-2);
}

歐拉函數:

二、組合數學?

楊輝三角?

dp法求組合數:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<vector<int>> dp;
int main(){ll n,m,c;cin>>n>>m>>c;dp.resize(n+1);for(int i=0;i<=n;i++){dp[i].resize(m+1);dp[i][0]=1;}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){dp[i][j]=(dp[i-1][j]+dp[i-1][j-1])%c;}}cout<<dp[n][m]<<endl;return 0;
}
const int N = 70;LL C[N][N];void init() {for (int i = 0; i < N; i ++) for (int j = 0; j <= i; j ++) if (!j) C[i][j] = 1;else C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr), cout.tie(nullptr);init();LL ans = 0;int n, m; cin >> n >> m;for (int i = n; i <= m; i ++) ans += C[m][i];cout << ans << endl;return 0;
}

預處理法求組合函數:僅僅適用于p為質數時候?

公式: p+q=p⊕q+2×(p&q)

這個公式的核心思想是:二進制加法可以分解為無進位加法(XOR)和進位(AND)的組合。?

    三、并查集

    1. 尋找根節點,函數:find(int u),也就是判斷這個節點的祖先節點是哪個
    2. 將兩個節點接入到同一個集合,函數:join(int u, int v),將兩個節點連在同一個根節點上
    3. 判斷兩個節點是否在同一個集合,函數:isSame(int u, int v),就是判斷兩個節點是不是同一個根節點
    int n = 1005; // n根據題目中節點數量而定,一般比節點數量大一點就好
    vector<int> father = vector<int> (n, 0); // C++里的一種數組結構// 并查集初始化
    void init() {for (int i = 0; i < n; ++i) {father[i] = i;}
    }
    // 并查集里尋根的過程
    int find(int u) {return u == father[u] ? u : father[u] = find(father[u]); // 路徑壓縮
    }// 判斷 u 和 v是否找到同一個根
    bool isSame(int u, int v) {u = find(u);v = find(v);return u == v;
    }// 將v->u 這條邊加入并查集
    void join(int u, int v) {u = find(u); // 尋找u的根v = find(v); // 尋找v的根if (u == v) return ; // 如果發現根相同,則說明在一個集合,不用兩個節點相連直接返回father[v] = u;
    }

    四、二維前綴和?

    #include<iostream>
    using namespace std;
    const int N = 1e4+10;
    int n,m,q;
    int a[N][N],s[N][N];int main(){scanf("%d %d %d",&n,&m,&q); for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);3for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)s[i][j]=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+a[i][j];while(q--){int l1,r1,l2,r2;scanf("%d %d %d %d",&l1,&r1,&l2,&r2);printf("%d",s[l2][r2]-s[l2][r1-1]-s[l1-1][r2]+s[l1-1][l2-1]);}return 0;
    }
    

    一維差分

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5 + 10;
    int a[N], diff[N]; // a為原數組,diff為差分數組// 插入操作:對區間 [l, r] 加上c
    void insert(int l, int r, int c) {diff[l] += c;diff[r + 1] -= c;
    }int main() {int n, m;cin >> n >> m;for (int i = 1; i <= n; i++) {cin >> a[i];// 初始化差分數組:將每個元素視為對自身區間的插入insert(i, i, a[i]);}while (m--) {int l, r, c;cin >> l >> r >> c;insert(l, r, c); // 應用區間修改}// 計算前綴和得到最終結果for (int i = 1; i <= n; i++) {diff[i] += diff[i - 1];cout << diff[i] << " ";}return 0;
    }

    二維差分?

    #include <iostream>
    using namespace std;
    const int N = 1010;
    int a[N][N], diff[N][N]; // a為原矩陣,diff為差分矩陣// 插入操作:對子矩陣 (x1,y1)-(x2,y2) 加上c
    void insert(int x1, int y1, int x2, int y2, int c) {diff[x1][y1] += c;diff[x2 + 1][y1] -= c;diff[x1][y2 + 1] -= c;diff[x2 + 1][y2 + 1] += c;
    }int main() {int n, m, q;cin >> n >> m >> q;for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {cin >> a[i][j];// 初始化差分矩陣:將每個元素視為對單點的插入insert(i, j, i, j, a[i][j]);}}while (q--) {int x1, y1, x2, y2, c;cin >> x1 >> y1 >> x2 >> y2 >> c;insert(x1, y1, x2, y2, c); // 應用子矩陣修改}// 計算二維前綴和得到最終矩陣for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {diff[i][j] += diff[i - 1][j] + diff[i][j - 1] - diff[i - 1][j - 1];cout << diff[i][j] << " ";}cout << endl;}return 0;
    }

    五、單調棧

    // 版本一
    class Solution {
    public:vector<int> dailyTemperatures(vector<int>& T) {// 遞增棧stack<int> st;vector<int> result(T.size(), 0);st.push(0);for (int i = 1; i < T.size(); i++) {if (T[i] < T[st.top()]) {                       // 情況一st.push(i);} else if (T[i] == T[st.top()]) {               // 情況二st.push(i);} else {while (!st.empty() && T[i] > T[st.top()]) { // 情況三result[st.top()] = i - st.top();st.pop();}st.push(i);}}return result;}
    };
    // 版本二
    class Solution {
    public:vector<int> dailyTemperatures(vector<int>& T) {stack<int> st; // 遞增棧vector<int> result(T.size(), 0);for (int i = 0; i < T.size(); i++) {while (!st.empty() && T[i] > T[st.top()]) { // 注意棧不能為空result[st.top()] = i - st.top();st.pop();}st.push(i);}return result;}
    };

    六、st表

    用于多次查詢區間中最大值或者最小值

    #include <iostream>
    #include <vector>
    #include <cmath>
    using namespace std;const int MAXN = 1e5 + 5;
    const int LOGN = 20;  // 覆蓋2^20的區間長度int st[MAXN][LOGN];   // ST表存儲最大值
    int log_table[MAXN];   // 預處理log2向下取整// 預處理log_table數組
    void init_log(int n) {log_table[1] = 0;for (int i = 2; i <= n; i++) log_table[i] = log_table[i/2] + 1;
    }// 構建ST表
    void build_st(const vector<int>& arr) {int n = arr.size();for (int i = 0; i < n; i++)st[i][0] = arr[i];  // 初始化長度為1的區間for (int j = 1; (1 << j) <= n; j++) {for (int i = 0; i + (1 << j) - 1 < n; i++) {int prev = 1 << (j-1);st[i][j] = max(st[i][j-1], st[i + prev][j-1]);}}
    }// 查詢區間[l, r]的最大值
    int query_max(int l, int r) {int k = log_table[r - l + 1];return max(st[l][k], st[r - (1 << k) + 1][k]);
    }int main() {int n, q;cout << "輸入數組長度和查詢次數: ";cin >> n >> q;vector<int> arr(n);cout << "輸入數組元素: ";for (int i = 0; i < n; i++) cin >> arr[i];init_log(n);     // 初始化log表build_st(arr);   // 構建ST表while (q--) {int l, r;cout << "輸入查詢區間[l, r] (0-based): ";cin >> l >> r;if (l > r) swap(l, r);cout << "最大值: " << query_max(l, r) << endl;}return 0;
    }

    七、樹狀數組

    動態求區間和

    #include <bits/stdc++.h>
    using namespace std;int n, m;           // 數據數量和操作次數
    int tree[2000010];  // 樹狀數組(大小通常開2*n)// 核心函數:獲取數字二進制表示中最低位的1對應的值
    int lowbit(int k) {return k & -k;  // 例如:k=6(110), lowbit(k)=2(10)
    }// 單點更新:在位置x處增加k
    void add(int x, int k) {while (x <= n) {          // 從葉子節點向上更新tree[x] += k;         // 更新當前節點x += lowbit(x);       // 跳轉到父節點(x += 最低位的1)}
    }// 前綴和查詢:計算[1, x]的區間和
    int sum(int x) {int ans = 0;while (x != 0) {          // 從右往左累加區間塊ans += tree[x];       // 累加當前節點值x -= lowbit(x);       // 跳轉到前驅節點(x -= 最低位的1)}return ans;
    }int main() {cin >> n >> m;// 初始化樹狀數組(等效于逐個插入元素)for (int i = 1; i <= n; i++) {int a;scanf("%d", &a);add(i, a);  // 將初始值插入樹狀數組}// 處理操作for (int i = 1; i <= m; i++) {int a, b, c;scanf("%d%d%d", &a, &b, &c);if (a == 1) {add(b, c);  // 在位置b處加c} else if (a == 2) {// 區間和 = [1,c]的和 - [1,b-1]的和cout << sum(c) - sum(b - 1) << endl;}}
    }
    • 八、字符串哈希

    • 原理:選擇兩個大素數作為基數(Base)和模數(MOD),計算哈希值后取模。
    • 公式
      hash[i]=(hash[i?1]×Base+str[i])mod??MODhash[i]=(hash[i?1]×Base+str[i])modMOD
      例如,Base=13,MOD=101時,字符串"abc"的哈希值為97?
    • 優點:沖突概率較低,適合多數場景。
    • #include<bits/stdc++.h>
      using namespace std;
      int n;
      const int base=131;
      long long mod=19260817;
      vector<int> hashnum;
      long long myhash(string s){int n=s.size();long long minhash;for(int i=0;i<n;i++){minhash=minhash*base+s[i]%mod;}return minhash;
      }
      int main(){cin>>n;hashnum.resize(n);for(int i=0;i<n;i++){string s;cin>>s;hashnum[i]=myhash(s);}sort(hashnum.begin(),hashnum.end());int ans=0;for(int i=0;i<n;i++){if(hashnum[i]!=hashnum[i-1]){ans++;}}cout<<ans;return 0;
      }
    • #include <iostream>
      #include <string>
      #include <vector>
      using namespace std;class StringHash {
      private:static const int Base = 131;       // 推薦的大素數,減少哈希沖突static const int MOD = 1e9 + 7;    // 大素數模數vector<long long> hash;             // 前綴哈希數組vector<long long> p;               // Base的冪次數組
      public:// 初始化哈希數組和冪次數組void init(const string &s) {int n = s.size();hash.resize(n + 1, 0);p.resize(n + 1, 1);for (int i = 1; i <= n; ++i) {p[i] = (p[i-1] * Base) % MOD;         // 計算Base^i % MODhash[i] = (hash[i-1] * Base + s[i-1]) % MOD;  // 前綴哈希計算}}// 獲取子串s[l..r)的哈希值(左閉右開區間)long long getHash(int l, int r) {if (l >= r) return 0;long long res = (hash[r] - hash[l] * p[r - l]) % MOD;return res < 0 ? res + MOD : res;         // 處理負數結果}
      };int main() {string s = "abcabc";StringHash sh;sh.init(s);// 示例:查詢子串哈希cout << "子串 [0,3) 的哈希值: " << sh.getHash(0, 3) << endl;  // abccout << "子串 [3,6) 的哈希值: " << sh.getHash(3, 6) << endl;  // abc// 判斷兩個子串是否相等if (sh.getHash(0, 3) == sh.getHash(3, 6)) {cout << "子串 abc 和 abc 哈希相等" << endl;}return 0;
      }

      九、圖論

    ????????拓補排序?

    vector<int> topologicalSort(int n, vector<vector<int>>& edges) {vector<vector<int>> adj(n + 1);  // 鄰接表(節點編號從1開始)vector<int> inDegree(n + 1, 0);  // 入度數組vector<int> result;              // 拓撲序列// 1. 建圖并統計入度for (auto& e : edges) {int u = e[0], v = e[1];adj[u].push_back(v);inDegree[v]++;}// 2. 初始化隊列(入度為0的節點)queue<int> q;for (int i = 1; i <= n; i++) {if (inDegree[i] == 0) q.push(i);}// 3. BFS處理拓撲排序while (!q.empty()) {int u = q.front();q.pop();result.push_back(u);for (int v : adj[u]) {inDegree[v]--;if (inDegree[v] == 0) q.push(v);}}// 4. 檢測環:若結果長度≠節點數,說明存在環if (result.size() != n) return {};return result;
    }

    ????????Floyd

    #include <bits/stdc++.h>
    using namespace std;const int N = 405;
    const int INF = 0x3f3f3f3f; // 避免INT_MAX溢出
    int graph[N][N];
    int n, m, q;int main() {// 初始化鄰接矩陣memset(graph, 0x3f, sizeof(graph)); // 快速初始化為INFcin >> n >> m >> q;for(int i=1; i<=n; i++) graph[i][i] = 0;// 輸入邊while(m--){int u, v, w;cin >> u >> v >> w;if(w < graph[u][v]){ // 處理重復邊graph[u][v] = w;graph[v][u] = w; // 無向圖}}// Floyd-Warshall算法for(int k=1; k<=n; k++){for(int i=1; i<=n; i++){for(int j=1; j<=n; j++){if(graph[i][k] != INF && graph[k][j] != INF){graph[i][j] = min(graph[i][j], graph[i][k] + graph[k][j]);}}}}// 查詢while(q--){int st, ed;cin >> st >> ed;cout << (graph[st][ed] == INF ? -1 : graph[st][ed]) << '\n';}return 0;
    }

    dijistra

    #include <bits/stdc++.h>
    using namespace std;
    typedef pair<int, int> PII;
    const int N = 1e5 + 10, INF = 0x3f3f3f3f;int n, m, s;        // 點數、邊數、起點
    int h[N], e[N], w[N], ne[N], idx;  // 鄰接表存圖
    int dist[N];        // 存儲最短距離
    bool vis[N];         // 標記是否已確定最短路徑void add(int a, int b, int c) {e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
    }void dijkstra() {memset(dist, 0x3f, sizeof dist);dist[s] = 0;priority_queue<PII, vector<PII>, greater<PII>> heap;heap.push({0, s});while (!heap.empty()) {auto t = heap.top();heap.pop();int ver = t.second, distance = t.first;if (vis[ver]) continue;  // 已確定最短路徑則跳過vis[ver] = true;// 遍歷鄰接點更新距離for (int i = h[ver]; i != -1; i = ne[i]) {int j = e[i];if (dist[j] > distance + w[i]) {dist[j] = distance + w[i];heap.push({dist[j], j});}}}
    }
    int main() {memset(h, -1, sizeof h);cin >> n >> m >> s;while (m--) {int a, b, c;cin >> a >> b >> c;add(a, b, c);  // 有向圖,無向圖需反向添加}dijkstra();for (int i = 1; i <= n; i++) cout << (dist[i] == INF ? -1 : dist[i]) << " ";return 0;
    }

    最小生成樹?

    prim找距離最小的點

    #include<iostream>
    #include<vector>
    #include <climits>using namespace std;
    int main() {int v, e;int x, y, k;cin >> v >> e;// 填一個默認最大值,題目描述val最大為10000vector<vector<int>> grid(v + 1, vector<int>(v + 1, 10001));while (e--) {cin >> x >> y >> k;// 因為是雙向圖,所以兩個方向都要填上grid[x][y] = k;grid[y][x] = k;}// 所有節點到最小生成樹的最小距離vector<int> minDist(v + 1, 10001);// 這個節點是否在樹里vector<bool> isInTree(v + 1, false);// 我們只需要循環 n-1次,建立 n - 1條邊,就可以把n個節點的圖連在一起for (int i = 1; i < v; i++) {// 1、prim三部曲,第一步:選距離生成樹最近節點int cur = -1; // 選中哪個節點 加入最小生成樹int minVal = INT_MAX;for (int j = 1; j <= v; j++) { // 1 - v,頂點編號,這里下標從1開始//  選取最小生成樹節點的條件://  (1)不在最小生成樹里//  (2)距離最小生成樹最近的節點if (!isInTree[j] &&  minDist[j] < minVal) {minVal = minDist[j];cur = j;}}// 2、prim三部曲,第二步:最近節點(cur)加入生成樹isInTree[cur] = true;// 3、prim三部曲,第三步:更新非生成樹節點到生成樹的距離(即更新minDist數組)// cur節點加入之后, 最小生成樹加入了新的節點,那么所有節點到 最小生成樹的距離(即minDist數組)需要更新一下// 由于cur節點是新加入到最小生成樹,那么只需要關心與 cur 相連的 非生成樹節點 的距離 是否比 原來 非生成樹節點到生成樹節點的距離更小了呢for (int j = 1; j <= v; j++) {// 更新的條件:// (1)節點是 非生成樹里的節點// (2)與cur相連的某節點的權值 比 該某節點距離最小生成樹的距離小// 很多錄友看到自己 就想不明白什么意思,其實就是 cur 是新加入 最小生成樹的節點,那么 所有非生成樹的節點距離生成樹節點的最近距離 由于 cur的新加入,需要更新一下數據了if (!isInTree[j] && grid[cur][j] < minDist[j]) {minDist[j] = grid[cur][j];}}}// 統計結果int result = 0;for (int i = 2; i <= v; i++) { // 不計第一個頂點,因為統計的是邊的權值,v個節點有 v-1條邊result += minDist[i];}cout << result << endl;}

    kruskal找最短邊

    
    #include <iostream>
    #include <vector>
    #include <algorithm>using namespace std;// l,r為 邊兩邊的節點,val為邊的數值
    struct Edge {int l, r, val;
    };// 節點數量
    int n = 10001;
    // 并查集標記節點關系的數組
    vector<int> father(n, -1); // 節點編號是從1開始的,n要大一些// 并查集初始化
    void init() {for (int i = 0; i < n; ++i) {father[i] = i;}
    }// 并查集的查找操作
    int find(int u) {return u == father[u] ? u : father[u] = find(father[u]); // 路徑壓縮
    }// 并查集的加入集合
    void join(int u, int v) {u = find(u); // 尋找u的根v = find(v); // 尋找v的根if (u == v) return ; // 如果發現根相同,則說明在一個集合,不用兩個節點相連直接返回father[v] = u;
    }int main() {int v, e;int v1, v2, val;vector<Edge> edges;int result_val = 0;cin >> v >> e;while (e--) {cin >> v1 >> v2 >> val;edges.push_back({v1, v2, val});}// 執行Kruskal算法// 按邊的權值對邊進行從小到大排序sort(edges.begin(), edges.end(), [](const Edge& a, const Edge& b) {return a.val < b.val;});// 并查集初始化init();// 從頭開始遍歷邊for (Edge edge : edges) {// 并查集,搜出兩個節點的祖先int x = find(edge.l);int y = find(edge.r);// 如果祖先不同,則不在同一個集合if (x != y) {result_val += edge.val; // 這條邊可以作為生成樹的邊join(x, y); // 兩個節點加入到同一個集合}}cout << result_val << endl;return 0;
    }
    

    BF?

    #include <iostream>
    #include <vector>
    #include <list>
    #include <climits>
    using namespace std;int main() {int n, m, p1, p2, val;cin >> n >> m;vector<vector<int>> grid;// 將所有邊保存起來for(int i = 0; i < m; i++){cin >> p1 >> p2 >> val;// p1 指向 p2,權值為 valgrid.push_back({p1, p2, val});}int start = 1;  // 起點int end = n;    // 終點vector<int> minDist(n + 1 , INT_MAX);minDist[start] = 0;for (int i = 1; i < n; i++) { // 對所有邊 松弛 n-1 次for (vector<int> &side : grid) { // 每一次松弛,都是對所有邊進行松弛int from = side[0]; // 邊的出發點int to = side[1]; // 邊的到達點int price = side[2]; // 邊的權值// 松弛操作 // minDist[from] != INT_MAX 防止從未計算過的節點出發if (minDist[from] != INT_MAX && minDist[to] > minDist[from] + price) { minDist[to] = minDist[from] + price;  }}}if (minDist[end] == INT_MAX) cout << "unconnected" << endl; // 不能到達終點else cout << minDist[end] << endl; // 到達終點最短路徑}

    SPFA?

    #include <cstring>
    #include <queue>
    using namespace std;const int MAXN = 1e5 + 5;  // 根據題目調整最大節點數
    const int INF = 0x3f3f3f3f; // 表示無窮大struct Edge {int v;  // 目標節點int w;  // 邊權
    };vector<Edge> e[MAXN];  // 鄰接表存圖
    int dis[MAXN];         // 起點到各點的最短距離
    int cnt[MAXN];         // 記錄到某節點的最短路邊數(用于負環檢測)
    bool vis[MAXN];        // 標記節點是否在隊列中// SPFA 主函數
    // 返回值: true=無負環,false=存在負環
    // 參數: n=總節點數,s=起點
    bool spfa(int n, int s) {// 初始化距離和隊列memset(dis, 0x3f, sizeof(dis));  // 初始化為INFmemset(cnt, 0, sizeof(cnt));memset(vis, 0, sizeof(vis));queue<int> q;dis[s] = 0;q.push(s);vis[s] = true;while (!q.empty()) {int u = q.front();q.pop();vis[u] = false;  // 出隊后取消標記// 遍歷所有鄰接邊for (auto &ed : e[u]) {int v = ed.v, w = ed.w;// 松弛操作:嘗試縮短 dis[v]if (dis[v] > dis[u] + w) {dis[v] = dis[u] + w;cnt[v] = cnt[u] + 1;  // 更新路徑邊數// 存在負環的判定條件if (cnt[v] >= n) return false;// 若v不在隊列中,則入隊if (!vis[v]) {q.push(v);vis[v] = true;}}}}return true;  // 未發現負環
    }

    十、動態規劃

    LCS最長公共子序列

    // 計算LCS長度
    int lengthOfLCS(string s1, string s2) {int m = s1.size(), n = s2.size();vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));for (int i = 0; i < m; ++i) {for (int j = 0; j < n; ++j) {if (s1[i] == s2[j]) {dp[i + 1][j + 1] = dp[i][j] + 1;} else {dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);}}}return dp[m][n];
    }// 輸出一個具體的LCS序列
    string getLCS(string s1, string s2) {int m = s1.size(), n = s2.size();vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));for (int i = 0; i < m; ++i) {for (int j = 0; j < n; ++j) {if (s1[i] == s2[j]) {dp[i + 1][j + 1] = dp[i][j] + 1;} else {dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);}}}string lcs;int i = m, j = n;while (i > 0 && j > 0) {if (s1[i - 1] == s2[j - 1]) {lcs.push_back(s1[i - 1]);i--, j--;} else if (dp[i - 1][j] > dp[i][j - 1]) {i--;} else {j--;}}reverse(lcs.begin(), lcs.end());return lcs;
    }

    LIS最長上升子序列

    / 計算LIS長度
    int lengthOfLIS_DP(vector<int>& nums) {int n = nums.size();vector<int> dp(n, 1);int max_len = 1;for (int i = 0; i < n; ++i) {for (int j = 0; j < i; ++j) {if (nums[j] < nums[i]) {dp[i] = max(dp[i], dp[j] + 1);}}max_len = max(max_len, dp[i]);}return max_len;
    }// 輸出一個具體的LIS序列
    vector<int> getLIS(vector<int>& nums) {int n = nums.size();vector<int> dp(n, 1), prev(n, -1);int max_len = 1, end_idx = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < i; ++j) {if (nums[j] < nums[i] && dp[i] < dp[j] + 1) {dp[i] = dp[j] + 1;prev[i] = j;}}if (dp[i] > max_len) {max_len = dp[i];end_idx = i;}}vector<int> lis;while (end_idx != -1) {lis.push_back(nums[end_idx]);end_idx = prev[end_idx];}reverse(lis.begin(), lis.end());return lis;
    }

    1. 01背包問題

    特點:每件物品只能選或不選(0或1次)。

    二維數組模板(時間復雜度O(NV),空間復雜度O(NV))
    int dp[N][V]; // N為物品數,V為背包容量
    for (int i = 1; i <= n; i++) {int w = weight[i], v = value[i];for (int j = 0; j <= V; j++) {if (j < w) dp[i][j] = dp[i-1][j];else dp[i][j] = max(dp[i-1][j], dp[i-1][j-w] + v);}
    }
    // 結果:dp[n][V]

    關鍵點:狀態從上一行(i-1)轉移而來。

    一維數組優化(空間復雜度O(V))
    int dp[V+1] = {0};
    for (int i = 1; i <= n; i++) {int w = weight[i], v = value[i];for (int j = V; j >= w; j--) { // 逆序遍歷防止覆蓋dp[j] = max(dp[j], dp[j-w] + v);}
    }
    // 結果:dp[V]

    關鍵點:逆序更新確保每個物品僅被計算一次。

    2. 完全背包問題

    特點:每件物品可選無限次。

    二維數組模板
    int dp[N][V];
    for (int i = 1; i <= n; i++) {int w = weight[i], v = value[i];for (int j = 0; j <= V; j++) {if (j < w) dp[i][j] = dp[i-1][j];else dp[i][j] = max(dp[i-1][j], dp[i][j-w] + v); // 區別:當前行轉移}
    }
    // 結果:dp[n][V]

    關鍵點:狀態從當前行轉移,允許重復選擇。

    一維數組優化
    int dp[V+1] = {0};
    for (int i = 1; i <= n; i++) {int w = weight[i], v = value[i];for (int j = w; j <= V; j++) { // 正序遍歷允許重復dp[j] = max(dp[j], dp[j-w] + v);}
    }
    // 結果:dp[V]

    關鍵點:正序更新允許多次選擇同一物品。

    3. 多重背包問題

    特點:第i種物品最多選s[i]次。

    二維數組模板(樸素版)
    int dp[N][V];
    for (int i = 1; i <= n; i++) {int w = weight[i], v = value[i], s = cnt[i];for (int j = 0; j <= V; j++) {dp[i][j] = dp[i-1][j];for (int k = 1; k <= s && k*w <= j; k++) {dp[i][j] = max(dp[i][j], dp[i-1][j-k*w] + k*v);}}
    }
    // 結果:dp[n][V]
    關鍵點:三層循環遍歷物品、容量和選取次數。
    一維數組優化(二進制拆分)
    #include <iostream>
    #include <vector>
    using namespace std;int main() {int N, W;cin >> N >> W;vector<int> dp(W + 1, 0);vector<pair<int, int>> items;  // 存儲拆分后的物品(重量,價值)for (int i = 0; i < N; i++) {int w, v, s;cin >> w >> v >> s;// 二進制拆分int k = 1;while (k <= s) {items.push_back({w * k, v * k});s -= k;k *= 2;}if (s > 0) {items.push_back({w * s, v * s});}}// 01 背包動態規劃for (auto &item : items) {int w = item.first, v = item.second;for (int j = W; j >= w; j--) {dp[j] = max(dp[j], dp[j - w] + v);}}cout << dp[W] << endl;return 0;
    }

    關鍵點:通過二進制拆分將問題轉化為01背包,降低復雜度

    十一、二分查找

    while(l<r)

    盡量向左找目標//在a數組中尋找第一個>=x的數,并返回其下標
    int bsearch_1(int l, int r)
    {while (l < r){int mid = l + r >> 1;//或寫成(l+r)/2if (a[mid]>=x) r = mid;else l = mid + 1;}return l;//因為while循環當l==r時終止,return r也可以
    }
    
    盡量向右找目標//在a數組中尋找最后一個<=x的數,并返回其下標
    int bsearch_2(int l, int r)
    {while (l < r){int mid = l + r + 1 >> 1;//或寫成(l+r+1)/2if (a[mid]<=x) l = mid;else r = mid - 1;}return l;//因為while循環當l==r時終止,return r也可以
    }
    
    #include <iostream>
    // 邊長數組,這樣我們就不用擔心數據裝不下了
    #include <vector>
    using namespace std;
    vector<int > vec;
    int main() {int n, m;int t;cin >> n >> m;int index;// 向數組尾部放入數據,因為這個特性,vector也可以當成stack用while(n--) cin >> t, vec.push_back(t);while(m--) {cin >> t;// 在序列里查找目標數據index = lower_bound(vec.begin(), vec.end(), t) - vec.begin();// 如果目標數據找到了,輸出答案,注意我們的數組下標是從0開始的if (t == vec[index]) cout << index + 1 << ' ';// 沒找到記得輸出-1哦else cout << -1 << ' ';}return 0;
    1. 核心功能差異
    函數返回值條件(升序序列)返回值條件(降序序列)示例(升序序列?{1,3,4,4,6}
    lower_bound第一個目標值的元素位置第一個目標值的元素位置lower_bound(4)?→ 指向第一個4(索引2)
    upper_bound第一個>目標值的元素位置第一個<目標值的元素位置upper_bound(4)?→ 指向6(索引4)

    關鍵點

    • 升序時lower_bound?包含等于,upper_bound?排除等于。
    • 降序時:需通過比較函數(如?greater<int>())反轉邏輯,此時?lower_bound?尋找第一個小于等于的元素,upper_bound?尋找第一個小于的元素 。
    #include <iostream>
    using namespace std;const int N = 100010;int n, q;
    int a[N];// 查找左邊界:第一個等于x的位置
    int find_left(int x) {int l = 0, r = n - 1;while (l <= r) {int mid = (l + r) >> 1;if (a[mid] >= x) r = mid - 1; // 向左逼近else l = mid + 1;}// 檢查是否找到return (l < n && a[l] == x) ? l : -1;
    }// 查找右邊界:最后一個等于x的位置
    int find_right(int x) {int l = 0, r = n - 1;while (l <= r) {int mid = (l + r) >> 1;if (a[mid] <= x) l = mid + 1; // 向右逼近else r = mid - 1;}// 檢查是否找到return (r >= 0 && a[r] == x) ? r : -1;
    }

    一、l <= r?模板(左閉右閉區間)

    1.?適用場景
    • 精確查找目標值是否存在
    • 尋找第一個滿足條件的元素(如第一個錯誤版本)
    • 適用于明確閉區間?[left, right]?的情況,確保所有元素都被覆蓋 。
    2.?代碼模板
    int binarySearch(vector<int>& nums, int target) {int l = 0, r = nums.size() - 1;  // 閉區間初始化while (l <= r) {                 // 終止條件:l > rint mid = l + (r - l) / 2;   // 防溢出寫法if (nums[mid] == target) {return mid;              // 找到目標值} else if (nums[mid] < target) {l = mid + 1;            // 目標在右半區間} else {r = mid - 1;            // 目標在左半區間}}return -1;                       // 未找到
    }
    3.?特點
    • 循環條件l <= r?確保所有元素被檢查,包括?l == r?的情況 。
    • 邊界更新:每次嚴格縮小范圍,避免死循環。
    • 典型應用:基礎二分查找(如 LeetCode 704)。

    二、l < r?模板(左閉右開區間)

    1.?適用場景
    • 尋找插入位置(如?lower_bound?/?upper_bound
    • 處理邊界問題(如第一個/最后一個滿足條件的元素)
    • 適用于左閉右開區間?[left, right),常用于迭代器或數組尾部。
      ?
    2.?代碼模板
    // 尋找第一個 >= target 的位置(lower_bound)
    int lowerBound(vector<int>& nums, int target) {int l = 0, r = nums.size();      // 右開區間初始化while (l < r) {                  // 終止條件:l == rint mid = l + (r - l) / 2;   // 防溢出寫法if (nums[mid] >= target) {r = mid;                // 目標在左半區間(包含 mid)} else {l = mid + 1;            // 目標在右半區間(排除 mid)}}return l;                        // 若未找到,返回插入位置
    }// 尋找第一個 > target 的位置(upper_bound)
    int upperBound(vector<int>& nums, int target) {int l = 0, r = nums.size();while (l < r) {int mid = l + (r - l) / 2;if (nums[mid] > target) {r = mid;} else {l = mid + 1;}}return l;  // 返回插入位置(最后一個等于 target 的下一個位置)
    }
    3.?特點
    • 循環條件l < r?確保區間范圍逐步縮小,最終?l == r
    • 邊界更新:右邊界?r?不包含在搜索范圍內,需注意最終結果的驗證(如?nums[l]?是否等于目標值)。
    • 典型應用:查找插入位置(LeetCode 35)或重復元素的邊界 。
      ?

    三、核心區別與選擇原則

    特性l <= r(閉區間)l < r(左閉右開)
    初始區間[0, n-1][0, n)
    終止條件l > r(所有元素已檢查)l == r(區間為空)
    更新邏輯mid ± 1?嚴格縮小范圍r = mid?或?l = mid + 1
    適用場景精確查找、存在性判斷邊界查找、插入位置、重復元素處理
    防溢出寫法mid = l + (r - l) / 2同上

    十三、高精度

    加法:

    #include <vector>
    #include <string>
    using namespace std;vector<int> add(vector<int>& A, vector<int>& B) {if (A.size() < B.size()) return add(B, A); // 保證A位數≥Bvector<int> C;int t = 0; // 進位for (int i = 0; i < A.size(); i++) {t += A[i];if (i < B.size()) t += B[i];C.push_back(t % 10);t /= 10;}if (t) C.push_back(t);return C;
    }// 調用示例
    string a = "999999999", b = "1";
    vector<int> A, B;
    for (int i = a.size()-1; i >= 0; i--) A.push_back(a[i]-'0');
    for (int i = b.size()-1; i >= 0; i--) B.push_back(b[i]-'0');
    vector<int> C = add(A, B);
    // 逆序輸出結果:for (int i = C.size()-1; i >= 0; i--) cout << C[i];

    減法:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=505;
    vector<int> a, b;// 修正比較函數:判斷a是否>=b
    bool cmp(vector<int>& x, vector<int>& y) { // 添加引用避免拷貝if (x.size() != y.size()) return x.size() > y.size();for (int i = x.size()-1; i >= 0; i--) {if (x[i] != y[i]) return x[i] > y[i];}return true;
    }// 修正減法函數:使用參數y的數據
    vector<int> sub(vector<int>& x, vector<int>& y) { // 添加引用避免拷貝vector<int> c;for (int i = 0, t = 0; i < x.size(); i++) {t = x[i] - t;if (i < y.size()) t -= y[i]; // 正確使用y[i]c.push_back((t + 10) % 10);t = (t < 0) ? 1 : 0;}while (c.size() > 1 && c.back() == 0) c.pop_back();return c;
    }int main() {string s1, s2;cin >> s1 >> s2;for (int i = s1.size()-1; i >= 0; i--) a.push_back(s1[i]-'0');for (int i = s2.size()-1; i >= 0; i--) b.push_back(s2[i]-'0');vector<int> c;if (cmp(a, b)) {c = sub(a, b);} else {cout << "-"; // 處理負號c = sub(b, a);}for (int i = c.size()-1; i >= 0; i--) cout << c[i];return 0;
    }

    乘法:

    #include <bits/stdc++.h>
    using namespace std;vector<int> mul(vector<int>& x, vector<int>& y) {vector<int> c(x.size() + y.size(), 0);for (int i = 0; i < x.size(); i++)for (int j = 0; j < y.size(); j++)c[i + j] += x[i] * y[j];int carry = 0;for (int i = 0; i < c.size(); i++) {c[i] += carry;carry = c[i] / 10;c[i] %= 10;}while (c.size() > 1 && c.back() == 0) c.pop_back();return c;
    }int main() {string s1, s2;cin >> s1 >> s2;vector<int> a, b;// 逆序存儲,個位在前for (int i = s1.size()-1; i >=0; i--) a.push_back(s1[i] - '0');for (int i = s2.size()-1; i >=0; i--) b.push_back(s2[i] - '0');vector<int> c = mul(a, b);// 逆序輸出for (int i = c.size()-1; i >=0; i--) cout << c[i];return 0;
    }

    十四、取消同步流

    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    //取消同步流
    

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/news/901213.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/news/901213.shtml
    英文地址,請注明出處:http://en.pswp.cn/news/901213.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    設計模式 --- 狀態模式

    狀態模式??是一種??行為型設計模式??&#xff0c;允許對象在內部狀態改變時動態改變其行為??&#xff0c;使對象的行為看起來像是改變了。該模式通過將狀態邏輯拆分為獨立類??&#xff0c;消除復雜的條件分支語句&#xff0c;提升代碼的可維護性和擴展性。 狀態模式的…

    【讀者求助】如何跨行業進入招聘崗位?

    文章目錄 讀者留言回信崗位細分1. 中介公司的招聘崗位2. 獵頭專員3. 公司的招聘專員選擇建議 面試建議1. 請簡單介紹你過去 3 年的招聘工作經歷&#xff0c;重點說下你負責的崗位類型和規模2. 你在招聘流程中最常用的渠道有哪些&#xff1f;如何評估渠道效果&#xff1f;3. 當你…

    AI Agent入門指南

    圖片來源網絡 ?一、開箱暴擊&#xff1a;你以為的"智障音箱"&#xff0c;其實是賽博世界的007? ?1.1 從人工智障到智能叛逃&#xff1a;Agent進化史堪比《甄嬛傳》? ?青銅時代&#xff08;2006-2015&#xff09;? “小娜同學&#xff0c;關燈” “抱歉&…

    pnpm 中 Next.js 模塊無法找到問題解決

    問題概述 項目在使用 pnpm 管理依賴時,出現了 “Cannot find module ‘next/link’ or its corresponding type declarations” 的錯誤。這是因為 pnpm 的軟鏈接機制在某些情況下可能導致模塊路徑解析問題。 問題診斷 通過命令 pnpm list next 確認項目已安裝 Next.js 15.2.…

    vulnhub:sunset decoy

    靶機下載地址https://www.vulnhub.com/entry/sunset-decoy,505/ 滲透過程 簡單信息收集 nmap 192.168.56.0/24 -Pn # 確定靶機ip&#xff1a;192.168.56.121 nmap 192.168.56.121 -A -T4 # 得到開放端口22,80 在80端口得到save.zip&#xff0c;需要密碼解壓。 john破解壓縮…

    代碼學習總結(一)

    代碼學習總結&#xff08;一&#xff09; 這個系列的博客是記錄下自己學習代碼的歷程&#xff0c;有來自平臺上的&#xff0c;有來自筆試題回憶的&#xff0c;主要基于 C 語言&#xff0c;包括題目內容&#xff0c;代碼實現&#xff0c;思路&#xff0c;并會注明題目難度&…

    OSPF的接口網絡類型【復習篇】

    OSPF在不同網絡環境下默認的不同工作方式 [a3]display ospf interface g 0/0/0 # 查看ospf接口的網絡類型網絡類型OSPF接口的網絡類型&#xff08;工作方式&#xff09;計時器BMA&#xff08;以太網&#xff09;broadcast &#xff0c;需要DR/BDR的選舉hello&#xff1a;10s…

    PHM學習軟件|PHM預測性維護系統

    使用步驟教程如下 1、登錄 用戶名&#xff1a;52phm 密碼&#xff1a;xxx &#xff08;區別在于不同用戶密鑰不一樣&#xff09; 2、上傳需要分析的數據集 支持數據集格式&#xff1a;csv、xlsx、xls、mat、json 3、主題1&#xff1a;機械參數計算 計算軸承、齒輪、皮帶的…

    MySQL MVCC 機制詳解

    MySQL MVCC 機制詳解 1. MVCC 基本概念 MVCC 是一種并發控制的方法&#xff0c;主要用于數據庫管理系統&#xff0c;允許多個事務同時讀取數據庫中的同一個數據項&#xff0c;而不需要加鎖&#xff0c;從而提高了數據庫的并發性能。 ┌──────────────────…

    Model Context Protocol (MCP) - 嘗試創建和測試一下MCP Server

    1.簡單介紹 MCP是Model Context Protocol的縮寫&#xff0c;是Anthropic開源的一個標準協議。MCP使得大語言模型可以和外部的數據源&#xff0c;工具進行集成。當前MCP在社區逐漸地流行起來了。同時official C# SDK(倉庫是csharp-sdk) 也在不斷更新中&#xff0c;目前最新版本…

    (三)行為模式:12、訪問者模式(Visitor Pattern)(C++示例)

    目錄 1、訪問者模式含義 2、訪問者模式的UML圖學習 3、訪問者模式的應用場景 4、訪問者模式的優缺點 5、訪問者模式C實現的實例 1、訪問者模式含義 訪問者模式&#xff08;Visitor Pattern&#xff09;是一種行為型設計模式&#xff0c;它允許將一個作用于某對象結構中的各…

    windows安卓子系統wsa隱藏應用列表的安裝激活使用

    Windows 11 安卓子系統應用部署全攻略 windows安卓子系統wsa隱藏應用列表的安裝激活使用|過檢測核心前端 在 Windows 11 系統中&#xff0c;安卓子系統為用戶帶來了在電腦上運行安卓應用的便利。經過一系列的操作&#xff0c;我們已經完成了 Windows 11 安卓子系統的底層和前端…

    Elasticsearch 集群搭建

    一、集群規劃 1.1 節點角色規劃 節點類型配置要求推薦數量Master節點低磁盤、中等CPU/內存3&#xff08;奇數防止腦裂&#xff09;Data節點高磁盤、高內存、多核CPU根據數據量擴展Coordinating節點高CPU/內存、低磁盤2&#xff08;可選&#xff09; 1.2 硬件建議 內存&…

    React 響應事件

    開發環境&#xff1a;Reacttsantd 使用 React 可以在 JSX 中添加 事件處理函數。其中事件處理函數為自定義函數&#xff0c;它將在響應交互&#xff08;如點擊、懸停、表單輸入框獲得焦點等&#xff09;時觸發。 學習內容 1.編寫事件處理函數的不同方法 2.如何從父組件傳遞事件…

    SQL基礎入門:從CRUD到JOIN再到索引(通俗易懂版)

    一、為什么需要SQL&#xff1f; 想象你在管理一個圖書館&#xff1a; 傳統方法&#xff1a;手動記錄每本書的位置、借閱者、歸還日期SQL方法&#xff1a;用數據庫系統自動管理&#xff0c;快速查詢《Java編程思想》在哪個書架 SQL&#xff08;Structured Query Language&…

    MINIQMT學習課程Day11

    現在開始進行策略的交易買賣分析&#xff1a; 還是之前的步驟&#xff0c;打開qmt&#xff0c;選擇獨立交易&#xff0c; 之后使用pycharm&#xff0c;編寫py文件 導入包&#xff1a; import time, datetime, traceback, sys from xtquant import xtdata from xtquant.xttr…

    # 實時人臉性別與年齡識別:基于OpenCV與深度學習模型的實現

    實時人臉性別與年齡識別&#xff1a;基于OpenCV與深度學習模型的實現 在當今數字化時代&#xff0c;計算機視覺技術正以前所未有的速度改變著我們的生活與工作方式。其中&#xff0c;人臉檢測與分析作為計算機視覺領域的重要分支&#xff0c;已廣泛應用于安防監控、智能交互、…

    Python Cookbook-5.14 給字典類型增加排名功能

    任務 你需要用字典存儲一些鍵和“分數”的映射關系。你經常需要以自然順序(即以分數的升序)訪問鍵和分數值&#xff0c;并能夠根據那個順序檢查一個鍵的排名。對這個問題&#xff0c;用dict 似乎不太合適。 解決方案 我們可以使用 dict 的子類&#xff0c;根據需要增加或者重…

    十四種邏輯器件綜合對比——《器件手冊--邏輯器件》

    目錄 邏輯器件 簡述 按功能分類 按工藝分類 按電平分類 特殊功能邏輯器件 應用領域 詳盡闡述 1 邏輯門 一、基本概念 二、主要類型 三、實現方式 四、應用領域 2 反相器 工作原理 基本功能 主要應用 常見類型 特點 未來發展趨勢 3 鎖存器 基本概念 工作原理 主要類型…

    如何更改wsl2中的ubuntu默認安裝位置

    先前的一篇文章提到了如何更改wsl里面ubuntu的home目錄&#xff0c;wsl裝ubuntu的home目錄在哪&#xff0c;如何更改home&#xff1f;_wsl安裝的ubuntu在哪里-CSDN博客 這次是要更改wsl中ubuntu的安裝目錄&#xff0c;畢竟默認安裝到c盤下會占用不少空間的。 從微軟商店get后…