百度松果菁英班OJ【連載】

第十六周

2 的 n 次冪

  • 高精度乘法

#include<bits/stdc++.h> 

using namespace std;

vector<int> mul(vector<int> &A) {
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size() || t; i++) {
        if (i < A.size()) t += A[i] * 2;
        C.push_back(t % 10);
        t /= 10;
    }
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

int main()
{
    int n;
    cin >> n;
    vector<int> A;
    A.push_back(1);
    while (n--) {
        A = mul(A);
    }
    for (int i = A.size() - 1; i >= 0; i--) {
        printf("%d", A[i]);
    }
    return 0;
}

個數統計

  • 高精度乘法求階乘
  • 個數統計
#include<bits/stdc++.h> 

using namespace std;

vector<int> mul(vector<int> &A, int x) {
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size() || t; i++) {
        if (i < A.size()) t += A[i] * x;
        C.push_back(t % 10);
        t /= 10;
    }
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

int main( )
{
    int N, a;
    cin >> N >> a;
    vector<int> A;
    A.push_back(1);
    for (int i = 2; i <= N; i++) {
        A = mul(A, i);
    }
    int ans = 0;
    for (int i = A.size() - 1; i >= 0; i--) {
        if (A[i] == a) ans++;
    }
    cout << ans << endl;
    return 0;
}

數組扞插

題目複述:

  • 將數組分為三部分,前半段,後半段,和中間的數(如果數組大小是奇數)
  • 前半段升序排列
  • 後半段降序排列
  • 如果有中間的數字,則中間的數不參與排列,直接放到結果數組的末尾
#include<bits/stdc++.h> 

using namespace std;

int n;
const int N = 10010;
int a[N], b[N];

int main( )
{
    cin >> n;
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    int l = n + 1 >> 1; // 前半段 + 中間數字(可能沒有)
    int r = n - l;      // 後半段
    if (l == r) {       // 前後一樣多
        sort(a, a + l, less<int>());
        sort(a + l, a + n, greater<int>());
    }
    else {              // 前面多一個,則中間的數不參與排序
        sort(a, a + l - 1, less<int>());
        sort(a + l, a + n, greater<int>());
    }
    int ll = 0, rr = l;

    // 交叉插入結果數組
    for (int i = 0; i < n; i++) {
        if (i % 2 == 0) b[i] = a[ll++];
        else b[i] = a[rr++];
    }
    for (int k = 0; k < n; k++) {
        printf("%d ", b[k]);
    }
    return 0;
}

MXR 競賽

題目複述:

  • N 個問題,都有積分,範圍為負數、零和正數
  • 選出所有一個積分子集,使得子集中的所有積分的乘積得到最大值

解法:

  • 貪心思想
  • 所有積分從小到大排序,所有正數全部參與乘積
  • 所有負數,相鄰兩個負數相乘得到正數。從左向右遍歷,只要相鄰兩個積分是負數,則這兩個負數都參與乘積;最後可能剩下一個絕對值最小的負數,不參與乘積
  • 特殊情況:如果所有積分都沒有選取,比如只有一個負數,返回數組最後一個數(最大)
#include<bits/stdc++.h> 

using namespace std;

const int N = 100010;
long n;
int a[N];

int main( )
{
    cin >> n;
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    long ans = 1;       // 注意精度,int 會爆精度
    int flag = 0;
    sort(a, a + n);
    for (int i = 0; i < n; i++) {
        if (a[i] < 0) {
            if (i < n - 1 && a[i + 1] < 0) {
                ans = ans * (a[i] * a[i + 1]);
                ans %= 998244353;
                i++;
                flag++;
            }
        } else if (a[i] > 0) {
            ans = ans * a[i];
            ans %= 998244353;
            flag++;
        }
    }
    if (flag == 0) printf("%d", a[n - 1]);
    else printf("%d", ans);
    return 0;
}

小碼哥的跳棋遊戲

題目複述:

  • 沒有棋子不消耗能量
  • 一次最多跳過一個棋子
  • 破壞一個棋子消耗一個能量
  • 求消耗最小能量從 0 位置到達 n 位置

解法:

  • 貪心思想 + 雙指針
  • 對於連續 n 個棋子,n 為奇數,最少破壞 ⌊n / 2⌋ 個棋子
  • 對於連續 n 個棋子,n 為偶數,最少破壞 n / 2 個棋子
  • 由於 int 除法自動取整特性,以上兩種情況可以合併
#include<bits/stdc++.h> 

using namespace std;

const int N = 100010;
int a[N];

int main( )
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    int ans = 0;
    int i = 0;
    while (i < n) {
        // 出現棋子
        if (a[i] == 1) {
            int count = 1;
            int j;
            // 統計連續棋子個數
            for (j = i + 1; j < n && a[j] == 1; j++) {
                 count++;
            }
            ans += count / 2;
            // 破壞之後就可以跳動了
            i = j;
        } else {
            // 沒有棋子就直接跳
            i++;
        }
    }
    // 由於跳動到第 n 塊,如果第 n - 1 塊是棋子,需要破壞
    if (a[n - 1] == 1) ans++;
    cout << ans;
    return 0;
}

矩陣乘法

  • 常規矩陣乘法
#include<bits/stdc++.h> 

using namespace std;

const int N = 1010;
int a[N][N], b[N][N], c[N][N];
int l, m, n;
void mul(int a[][N], int b[][N]) {
    for (int i = 0; i < l; i++) {
        for (int j = 0; j < n; j++) {
            for (int k =0; k < m; k++) {
                c[i][j] += a[i][k] * b[k][j];
            }
        }
    }
}

int main( )
{
    cin >> l >> m >> n;
    for (int i = 0; i < l; i++) {
        for (int j = 0; j < m; j++) {
            scanf("%d", &a[i][j]);
        }
    }
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            scanf("%d", &b[i][j]);
        }
    }
    mul(a, b);
    for (int i = 0; i < l; i++) {
        for (int j = 0; j < n; j++) {
            printf("%d ", c[i][j]);
        }
        printf("\n");
    }
    return 0;
}