SDU 程序設計思維實踐 第四周 csp模擬

題目A - 咕咕東的奇遇

題意

咕咕東是個貪玩的孩子,有一天,他從上古遺蹟中得到了一個神奇的圓環。這個圓環由字母表組成首尾相接的環,環上有一個指針,最初指向字母a。咕咕東每次可以順時針或者逆時針旋轉一格。例如,a順時針旋轉到z,逆時針旋轉到b。咕咕東手裏有一個字符串,但是他太笨了,所以他來請求你的幫助,問最少需要轉多少次。

在這裏插入圖片描述

Input

zeus

Output

18

思路

考慮上衣字符撥到當前字符逆時針還是順時針即可。

具體的判斷可以用,注意取模:

  • 順時針: 當前字符- 上一字符
  • 逆時針:26- (當前字符- 上一字符)

總結

憨憨的我上來直接24個英文字母。簽到題

代碼

#include <iostream>
#include <cstdio>
using namespace std;
int main() {
    string s;
    cin >> s;
    int ans = 0;
    int k = 0;
    for (int i = 0; i < s.size(); i++) {
        int cur = (26 + s[i] - 'a' - k) % 26;
        // ans += min((s[i] - 'a' - k) % 26, 26 - ((s[i] - 'a' - k) % 26));
        ans += min(cur, 26 - cur);
        k = s[i] - 'a';
        // cout << k << " " << ans << endl;
    }
    cout << ans;
    return 0;
}

題目B - 咕咕東想喫飯

題意

咕咕東考試周開始了,考試周一共有nn天。他不想考試周這麼累,於是打算每天都喫頓好的。他決定每天都喫生煎,咕咕東每天需要買aia_i個生煎。但是生煎店爲了刺激消費,只有兩種購買方式:①在某一天一次性買兩個生煎。②今天買一個生煎,同時爲明天買一個生煎,店家會給一個券,第二天用券來拿。沒有其餘的購買方式,這兩種購買方式可以用無數次,但是咕咕東是個節儉的好孩子,他訓練結束就走了,不允許訓練結束時手裏有券。咕咕東非常有錢,你不需要擔心咕咕東沒錢,但是咕咕東太笨了,他想問你他能否在考試周每天都能恰好買aia_i個生煎。

其中

(1n100000)(1 \le n \le 100000)(1ai10000)(1 \le a_i \le 10000)

Input

4
1 2 1 2

Output

2

思路

考慮對於第ii天,假設買了m個煎餅,其第二種方案對第i+1i+1天的影響可以轉變爲至多一次。

因爲,假設第ii天選了2次方案二,其等價於第ii天選1次方案一,第i+1i + 1天選1次方案一。

故問題可以轉化爲先考慮最後一天,若爲偶數個,直接全選擇方案1,若爲奇數個,選擇一次方案1。並讓前一天總煎餅數-1(選擇一次方案2)。


則問題轉化爲,對於當前天:

  • 若煎餅數爲偶數,則繼續。
  • 若煎餅數爲基數,則令前一天煎餅數-1。

輸出NO的情況爲當前天煎餅數<0<0 ,或第一天煎餅數爲基數個。否則輸出YES

總結

這題還是挺有意思的,不過數據有點水(逃) 。全輸出YES能拿到不少分吧。

代碼

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
using namespace std;
int v[100010];
int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> v[i];
    }
    int flag = 0;
    for (int i = n; i; i--) {
        if (flag == 1) {
            v[i] -= 1;
            if (v[i] < 0) {
                cout << "NO";
                return 0;
            }
        }
        if (v[i] % 2 == 0) {
            flag = 0;
        } else {
            flag = 1;
        }
    }
    if (flag == 1) {
        cout << "NO";
    } else {
        cout << "YES";
    }
    return 0;
}

題目C - 可怕的宇宙射線

題意

衆所周知,瑞神已經達到了CS本科生的天花板,但殊不知天外有天,人外有苟。在浩瀚的宇宙中,存在着-種叫做苟狗的生物, 這種生物天生就能達到人類研究生的知識水平,並且天生擅長CSP,甚至有全國第一的水平!但最可怕的是,它可以發出宇宙射線!宇宙射線可以摧毀人的智商,進行降智打擊!

宇宙射線會在無限的二維平面上傳播(可以看做一個二維網格圖),初始方向默認向上。宇宙射線會在發射出一段距離後分裂,向該方向的左右4545^{\circ}方向分裂出兩條宇宙射線,同時威力不變!宇宙射線會分裂nn次,每次分裂後會在分裂方向前進aa個單位長度。

現在瑞神要帶着他的小弟們挑戰苟狗,但是瑞神不想讓自己的智商降到普通本科生zjm那麼菜的水平,所以瑞神來請求你幫他計算出共有多少個位置會被"降智打擊”。

在這裏插入圖片描述

Input

4
4 2 3 2

Output

39

思路

樸素的DFS,和BFS能拿到40分。據說剪枝後可以A掉,但是要注意考慮層數,(第4次和21次以同一方向道道某個點並不能剪掉),否則會WA。下面給出一種好的解法(這裏感謝下hf大佬。

首先考慮分裂30次,2302^{30}顯然會TLE。考慮每次分裂是對稱的,其實我們只需要考慮一半就行,如只考慮向右邊分裂,把分裂後的圖沿着對稱軸對稱過去,這樣問題變成了30次圖的對稱複製。

至於怎麼對稱,考慮:

在這裏插入圖片描述

我們已知線段L,點A,求點A的對稱點B。

那麼問題就很簡單了,高數學過(我卻忘了,LY老師Dbq):

  • 對於上、下、左、右。很簡單
  • 對於其它方向,用下式推導,結果很好看。

在這裏插入圖片描述

總結

set用法
這題我算錯複雜度了,以爲樸素的dfs就能A。(問問自己,第幾次這樣了???)。然後玩了半小時(RNG NB)。最後看出來怎麼做了,奈何不會算對稱點,最後拿了暴力的40分,只能補題了。

代碼

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
struct re {
    int x, y;
    bool operator<(const re& a) const { return x != a.x ? x < a.x : y < a.y; }
};
set<re> M;
int ans;
int fx[] = {0, 1, 1, 1, 0, -1, -1, -1};
int fy[] = {1, 1, 0, -1, -1, -1, 0, 1};
int v[50], n;
void dfs(re cur, int k, int flag) {
    if (k > n) return;
    // cout << k << " " << flag << endl;
    dfs({cur.x + fx[flag] * v[k], cur.y + fy[flag] * v[k]}, k + 1, (flag + 1) % 8);
    set<re> temp;
    for (auto& i : M) {
        if (flag == 0 || flag == 4)
            temp.insert({cur.x * 2 - i.x, i.y});
        else if (flag == 1 || flag == 5)
            temp.insert({i.y + cur.x - cur.y, i.x - cur.x + cur.y});
        else if (flag == 2 || flag == 6)
            temp.insert({i.x, cur.y * 2 - i.y});
        else if (flag == 3 || flag == 7)
            temp.insert({cur.x + cur.y - i.y, cur.x + cur.y - i.x});
    }
    M.insert(temp.begin(), temp.end());
    for (int i = 1; i <= v[k]; i++) {
        M.insert({cur.x + fx[flag] * i, cur.y + fy[flag] * i});
    }
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> v[i];
    }
    dfs({0, 0}, 1, 0);
    cout << M.size();
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章