APCS 2022年1月題解

這次去考了 $\text{APCS}$,不知道會有幾分,不過在 $\text{Zerojudge}$ 上都過了,就來寫個題解吧。

$\text{p1:}$ 程式交易 (Zerojudge_h081)

題目大意:在時刻 $1\sim n$ 有 $n$ 張價錢為 $a_i$ 的股票,第一天強制要買,之後幾天,如果手中持有股票,並且當天股票價格大於等於手中股票價值 $+D$,就強制售出;如果手中未持有股票,且當天股票價格小於等於上次售出股票價值 $-D$,就強制購入。問賺到多少錢,目前手中股票可忽視。

解法:用一個 $\text{last}$ 紀錄目前手中股票或是上次售出股票價值,並使用一個布林變數 $buy$ 紀錄現在手上是否持有股票,最後在答案加總。

$\text{Code:}$

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

int main() {
    ios::sync_with_stdio (false), cin.tie (0);
    int n, d, ans = 0, last;
    bool buy = 0;
    cin >> n >> d;
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        if (i == 1)
            last = x, buy = 1;
        else if (buy && x >= last + d)
            ans += x - last, last = x, buy = 0;
        else if (!buy && x <= last - d)
            last = x, buy = 1;
    }
    cout << ans << '\n';
}

$\text{p2:}$ 贏家預測 (Zerojudge_h082)

題目大意:有 $n$ 個人編號 $1\times n$,每個人有各自的 $a,b$ 值,一開始會給一個打架順序,由左到右每兩個人一組打,最後一個人打不到架算直接獲勝。打架獲勝機制如下:如果 $a_1\times b_1≥a_2\times b_2$,第一個人獲勝,$a_1' = a_1 + a_2\times b_2/(2\times b_1)$,$b_1' = b_1 + a_2\times b_2/(2\times a_1)$,$a_2' = a_2 + a_2/2$,$b_2' = b_2 + b_2/2$,反之亦然。如果這個人累積失敗次數 $≥m$,就直接淘汰。新的隊伍先加入獲勝的人,再加入失敗但是沒被淘汰的人,如此下去直到剩一人為贏家。

解法:可以定義一個 $\text{struct}$,裡面存編號、$a,b$ 值、失敗次數,用一個 $\text{vector}$ 去存隊伍,用一些函數進行操作就可以了。

$\text{Code:}$記得要使用 $\text{long long}$

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

struct People {
    long long id, a, b, cnt = 0;
};

int n, m;
vector<People> v;

bool hit (int x, int y) {
    long long a = v[x].a, b = v[x].b, c = v[y].a, d = v[y].b;
    if (a * b >= c * d) {
        v[y].cnt++;
        v[x].a = a + c * d / (2 * b);
        v[x].b = b + c * d / (2 * a);
        v[y].a = c + c / 2;
        v[y].b = d + d / 2;
        return 1;
    } else {
        v[x].cnt++;
        v[y].a = c + a * b / (2 * d);
        v[y].b = d + a * b / (2 * c);
        v[x].a = a + a / 2;
        v[x].b = b + b / 2;
        return 0;
    }
}

void fight() {
    bool win[v.size()];
    fill (win, win + v.size(), 1);
    for (int i = 0; i < v.size(); i += 2) {
        if (i == v.size() - 1)
            break;
        if (hit (i, i + 1))
            win[i + 1] = 0;
        else
            win[i] = 0;
    }
    vector<People> tmp;
    for (int i = 0; i < v.size(); i++) {
        if (win[i])
            tmp.push_back (v[i]);
    }
    for (int i = 0; i < v.size(); i++) {
        if (!win[i] && v[i].cnt < m)
            tmp.push_back (v[i]);
    }
    v = tmp;
}

int main() {
    ios::sync_with_stdio (false), cin.tie (0);
    cin >> n >> m;
    People p[n + 1];
    for (int i = 1; i <= n; i++)
        cin >> p[i].a, p[i].id = i;
    for (int i = 1; i <= n; i++)
        cin >> p[i].b;
    for (int i = 1; i <= n; i++) {
        int id;
        cin >> id;
        v.push_back (p[id]);
    }
    while (v.size() > 1)
        fight();
    cout << v[0].id << '\n';
}

$\text{p3:}$ 數位占卜 (Zerojudge_h083)

題目大意:給你 $n$ 個字串,問你有幾對字串可以組成一個新字串,且此字串前半部分與後半部分相同。

解法:我是用 $\text{unordered_map}$,枚舉每個字串的前半部分跟後半部分的一小部分,看有沒有字串符合條件。

$\text{Code:}$

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

unordered_map<string, bool> hs;

int main() {
    ios::sync_with_stdio (false), cin.tie (0);
    int n;
    cin >> n;
    string s[n + 1];
    for (int i = 1; i <= n; i++)
        cin >> s[i], hs[s[i]] = 1;
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        for (int len = 1; len < s[i].size(); len++) {
            string s1, s2;
            s1 = s[i].substr (0, s[i].size() - len);
            s2 = s[i].substr (s[i].size() - len, len);
            if (s2.size() >= s1.size())
                break;
            string s3, s4;
            s3 = s[i].substr (0, s2.size());
            s4 = s[i].substr (s2.size(), s1.size() - s2.size());
            if (s2 == s3)
                ans += hs.count (s4);
        }
    }
    cout << ans << '\n';
}

$\text{p4:}$牆上海報 (Zerojudge_h084)

題目大意:有 $n$ 個木條,高度分別為 $h_1\sim h_n$,有 $k$ 張海報,高度為 $1$,寬度分別為 $w_1\sim w_k$,問海報從左往右按照順序貼在相同高度,且要貼在木條上,不可重疊,可以貼完又最高,高度可以是多少。

解法:可以發現貼在底下一定可以貼完,越往上就會發現越難貼完,或許可以發現,在答案求的最大高度以下的高度都可以成功貼完(因為貼的空間會越來越大),超過的就都不行。對於這種判定特定高度是否符合條件很容易,直接想貪心找出答案很困難的問題,我們可以使用二分搜尋法找答案。設定 $l,r$ 為上界與下界,每次看中間值 $mid$ 符不符合,如果符合,可使 $l=mid$,否則使 $r=mid-1$,最後當 $l$ 與 $r$ 相同時,$l = r =$ 答案。

$\text{Code:}$

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

const int SIZE = 2e5 + 5;

int n, k, h[SIZE], w[SIZE];

bool ok (int x) {
    int now = 1, len = 0;
    for (int i = 1; i <= n; i++) {
        if (h[i] >= x)
            len++;
        else
            len = 0;
        if (len >= w[now])
            len -= w[now], now++;
        if (now > k)
            return 1;
    }
    return 0;
}

int main() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
        cin >> h[i];
    for (int i = 1; i <= k; i++)
        cin >> w[i];
    int l = 0, r = *max_element (h + 1, h + n + 1);
    while (l < r) {
        int mid = l + (r - l) / 2 + 1;
        if (ok (mid))
            l = mid;
        else
            r = mid - 1;
    }
    cout << l << '\n';
}

我的分享就到這裡結束了,如果喜歡我的 $\text{Blog}$,歡迎追蹤!

留言

熱門文章