1. 程式人生 > 其它 >Good Bye 2021: 2022 is NEAR

Good Bye 2021: 2022 is NEAR

A - Integer Diversity

題目大意

給定一個數組, 每次可以選擇任意長度的字串取反, 問最後能有多少不同的數字

思路

很簡單, 用set記錄每一個值是否出現過, 沒有就插入, 有就取反然後插入

程式碼

#include <iostream>
#include <set>
 
using namespace std;
 
int main() {
    int T;
    cin >> T;
    while (T --) {
        int n;
        cin >> n;
        set<int> s;
        while (n --) {
            int x;
            cin >> x;
            if (s.count(x)) s.insert(-x);
            else s.insert(x);
        }
        cout << s.size() << endl;
    }
    return 0;
}

B - Mirror in the String

題目大意

給定一個字串, 找一個字首, 使其字首和字首反串的字典序最小

思路

首先, 選擇的字首一定是不增的
但是如果開始有兩個以上的相同字元, 那麼必然是取一個就好
否則, 依次向後查詢, 直到遇到第一個更大的字元

程式碼

#include <iostream>
#include <set>
#include <algorithm>
 
using namespace std;
 
int main() {
    int T;
    cin >> T;
    while (T --) {
        int n;
        cin >> n;
        string str;
        cin >> str;
        if (n == 1 || str[0] == str[1]) cout << str[0] << str[0] << endl;
        else {
            int i = 0;
            while (i < str.length() && str[i + 1] <= str[i]) i ++;
            string a = str.substr(0, i + 1);
            cout << a;
            reverse(a.begin(), a.end());
            cout << a << endl;
        }
    }
    return 0;
}

C - Representative Edges

題目大意

給定一個長度為n的陣列, 問最少需要改變多少數字, 可以使任意的l < r滿足 \(a_l + a_{l + 1} + \ldots + a_r = \frac{1}{2}(a_l + a_r) \cdot (r - l + 1)\)

思路

是在看過樣例之後發現, 最後的結果總是變為一個等差數列
開始想, 是否可以列舉起點公差, 但是存在精度問題
於是, n是很小的, 直接三重列舉, 判斷在ij之間有多少k滿足 \((a_k - a_i) \cdot (k - j) == (a_k - a_j) \cdot (k - i)\)


當然也要化除為乘

程式碼

#include <iostream>
#include <set>
#include <algorithm>
 
using namespace std;
 
int n;
int a[100];
 
int main() {
    int T;
    cin >> T;
    while (T --) {
        cin >> n;
        for (int i = 1; i <= n; i ++) cin >> a[i];
 
        int ans = 1;
        for (int i = 1; i <= n; i ++)
            for (int j = i + 1; j <= n; j ++) {
                int cnt = 0;
                for (int k = 1; k <= n; k ++)
                    if (k == i || k == j || (a[k] - a[i]) * (k - j) == (a[k] - a[j]) * (k - i))
                        cnt ++;
                ans = max(ans, cnt);
            }
        cout << n - ans << endl;
    }
    return 0;
}

D - Keep the Average High

題目大意

給定一個長度為n的陣列, 在其中選擇最多的下標使得, 任意l < r滿足

  • 區間內至少有一個數沒有被選擇
  • 或者, 總和大於 長度 * X

思路

不知道為什麼就想到DP了
定義一個數組f[N][2], 表示到當前位置, 當前位置選或不選的最大結果數
狀態轉移好像有些不清晰了

  • 首先當前位置不選的話
  • 上一個位置選和不選都可以, 取最大值
  • 當前位置選
  • 上一個位置不選是可以的
  • 寫了幾次發現, 當前位置只會和前兩個位置產生轉移, 再前面就不會影響了
  • 轉移的條件必然是和上一個位置的和大於2 * X
  • 如果當前位置大於X, 那麼上一個位置就是可選的, 這裡為什麼不討論上二個是因為 選上一個已經討論過是否選上二個了
  • 如果是上一個位置大於X, 那麼必須注意這三個位置的和是否也是大於3 * X
  • 這裡首先是可以從上二個不選轉移
  • 然後就是大於3 * X的話, 上二個也是可以被選擇的
    這裡有個技巧是多判斷一個, 然後答案直接取多判斷那個不取的情況

程式碼

#include <iostream>
#include <set>
#include <algorithm>

using namespace std;

const int N = 5e4 + 10;

int n, x;
int a[N];
int f[N][2];

int main() {
    int T;
    cin >> T;
    while (T --) {
        cin >> n;
        for (int i = 1; i <= n; i ++) cin >> a[i];
        cin >> x;

        f[1][0] = 0, f[1][1] = 1;
        for (int i = 2; i <= n + 1; i ++) {
            f[i][0] = max(f[i - 1][0], f[i - 1][1]);
            f[i][1] = f[i - 1][0] + 1;
            if (a[i] >= x && a[i] + a[i - 1] >= 2 * x) f[i][1] = max(f[i][1], f[i - 1][1] + 1);
            else {
                if (a[i - 1] >= x && a[i] + a[i - 1] >= 2 * x) {
                    if (i == 2) f[i][1] = 2;
                    else {
                        f[i][1] = max(f[i][1], f[i - 2][0] + 2);
                        if (a[i - 2] + a[i - 1] >= 2 * x && a[i - 2] + a[i - 1] + a[i] >= 3 * x)
                            f[i][1] = max(f[i][1], f[i - 2][1] + 2);
                    }
                }
            }
        }
        cout << f[n + 1][0] << endl;
    }
    return 0;
}