2019年CSP-J復賽題解


題目涉及算法:

  • 數字游戲:字符串入門題;
  • 公交換乘:模擬;
  • 紀念品:完全背包;
  • 數字游戲:廣搜/最短路。

數字游戲

題目鏈接:https://www.luogu.com.cn/problem/P5660
題目大意:求一個長度為8的字符串中有多少個字符0。
題解:
首先開一個變量用於計數(我稱此變量為“計數器”,初始時計數器的值為0)。
然后輸入字符串,然后從0到7遍歷字符串的前8個字符,每當遇到一個字符‘1’就將計數器加一,最后輸出計數器對應的值即可。
實現代碼如下:

#include <bits/stdc++.h>
using namespace std;
char s[10];
int cnt;
int main() {
    cin >> s;
    for (int i = 0; i < 8; i ++)
        if (s[i] == '1')
            cnt ++;
    cout << cnt << endl;
    return 0;
}

公交換乘

題目鏈接:https://www.luogu.com.cn/problem/P5661
題目大意:
有公交和地鐵,做地鐵會得到一張票,憑此票可以在45分鍾內免費坐一班票價小於等於地鐵票的公交車,
默認按照最早的一張符合要求的地鐵車票開始使用。問按照這種操作,小軒出行的總花費。
題解:
因為條件是限定死的,即給了我乘車的記錄,答案是固定的。所以不管我使用什么方法,只要能解決這樣一個過程就可以了。
本題如果將條件中的時間間隔從 45 分鍾修改為 n 分鍾,那么是可以使用線段樹+二分做的。
然而題目給我們的時間間隔是固定的 45 分鍾,所以我們可以采取更暴力的手段來解決這個問題。
我們只需要從前往后遍歷每一張車票,

  • 如果這張車票是地鐵票,則我們的總花費直接加上地鐵票的價格;
  • 如果這張查票是公交車票,則我們從它的45分鍾之前開始找是否有滿足要求的地鐵票可以使用的,如果有,則使用該地鐵票並且置本次公交車免費;否則,總價格需要加上公交車的車費。

因為題目限定了時間從早到晚,並且每班車的開始時間都不一樣,所以對於第 i 班車,我們只需要從第 max(1, i-45) 班車開始遍歷到第 i-1 班車就可以了。
所以總的時間復雜度是 O(45n) 。

實現代碼如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n,  // 乘車記錄的數量
    type[maxn],     // 交通工具類型
    price[maxn],    // 乘車的票價
    t[maxn],        // 開始乘車時間
    tot;            // 總花費
int main() {
    cin >> n;
    for (int i = 1; i <= n; i ++) cin >> type[i] >> price[i] >> t[i];
    for (int i = 1; i <= n; i ++) {
        if (type[i] == 0) tot += price[i];
        else {
            bool flag = false;
            for (int j = max(1, i-45); j < i; j ++) {
                if (type[j] == 0 && t[i]-t[j] <= 45 && price[j] >= price[i]) {
                    type[j] = 1;
                    flag = true;
                    break;
                }
            }
            if (!flag) tot += price[i];
        }
    }
    cout << tot << endl;
    return 0;
}

紀念品

題目鏈接:https://www.luogu.com.cn/problem/P5662
題目大意:
有 n 件物品,它們每一天都有一個價格,然后現在有T天,每一天你都可以選擇賣出你手頭上有的任意物品任意件,也可以選擇以當天的價格買入任意物品任意件。
題解:
完全背包變形題。
我們枚舉任意兩天 i 和 j(i小於j),那么在已知第i天我能夠獲得的總價值是 Vi 的情況下,我們可以進行一次完全背包:
背包的容量為 Vi,同時我們令 Fi 表示容量為i的背包的最大價值,那么第i天能夠擴充第j天背包的最大容量應該是 max(Fk + Vi-k) ,然后我們再選該候選項和當前 Vj 的最大值作為 Vj 的選項即可。

實現代碼如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10010;
int n, T, a[101][101], V[101], f[maxn];
void solve(int day1, int day2) {
    memset(f, 0 ,sizeof(int)*(V[day1]+1));
    for (int i = 1; i <= n; i ++) {
        int c = a[day1][i], w = a[day2][i];
        for (int j = c; j <= V[day1]; j ++) {
            f[j] = max(f[j], f[j-c] + w);
        }
    }
    int tmp = 0;
    for (int i = 0; i <= V[day1]; i ++) tmp = max(tmp, f[i] + V[day1]-i);
    V[day2] = max(V[day2], tmp);
}
int main() {
    cin >> T >> n >> V[0];
    for (int i = 1; i <= T; i ++) for (int j = 1; j <= n; j ++) cin >> a[i][j];
    for (int i = 1; i <= T; i ++) {
        V[i] = max(V[i], V[i-1]);
        for (int j = i+1; j <= T; j ++) {
            solve(i, j);
        }
    }
    cout << V[T] << endl;
    return 0;
}

加工零件

題目鏈接:https://www.luogu.com.cn/problem/P5663
題目大意:
凱凱的工廠正在有條不紊地生產一種神奇的零件,神奇的零件的生產過程自然也很神奇。工廠里有 n 位工人,工人們從 1∼n 編號。某些工人之間存在雙向的零件傳送帶。保證每兩名工人之間最多只存在一條傳送帶。
如果 x 號工人想生產一個被加工到第 L(L>1) 階段的零件,則所有與 x 號工人有傳送帶直接相連的工人,都需要生產一個被加工到第 L−1 階段的零件(但 x 號工人自己無需生產第 L−1 階段的零件)。
如果 x 號工人想生產一個被加工到第 1 階段的零件,則所有與 x 號工人有傳送帶直接相連的工人,都需要為 x 號工人提供一個原材料。
軒軒是 1 號工人。現在給出 q 張工單,第 i 張工單表示編號為 a_i的工人想生產一個第 L_i 階段的零件。軒軒想知道對於每張工單,他是否需要給別人提供原材料。他知道聰明的你一定可以幫他計算出來!
題解:
將每個點拆分成奇點和偶點,從1號偶點求到每個點的最短路。
然后對於每次詢問的距離L和點u,如果L是偶數並且到點u偶點的距離小於等於L,或者L是奇數並且到點u奇點的距離小於等於L,那么就能夠走通。
但是還有一組特例,就是如果點1的度數為0(即沒有連向點1的邊)並且詢問的點也是1,那也沒有辦法走到,因為起點沒有邊,需要特殊判斷這個條件。
實現代碼如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100100;
int dis[maxn][2], n, m, q;
vector<int> g[maxn];
struct Node {
    int u, s;
    Node (int _u, int _s) { u = _u; s = _s; }
};
queue<Node> que;
bool inq[maxn][2];
void spfa() {
    memset(inq, 0, sizeof(inq));
    memset(dis, -1, sizeof(dis));
    dis[1][0] = 0;
    que.push(Node(1, 0));
    while (!que.empty()) {
        Node nd = que.front();
        que.pop();
        int u = nd.u, s = nd.s;
        inq[u][s] = false;
        int sz = g[u].size();
        for (int i = 0; i < sz; i ++) {
            int v = g[u][i];
            if (dis[v][s^1] == -1 || dis[v][s^1] > dis[u][s] + 1) {
                dis[v][s^1] = dis[u][s] + 1;
                if (!inq[v][s^1]) {
                    inq[v][s^1] = true;
                    que.push(Node(v, s^1));
                }
            }
        }
    }
}
int main() {
    cin >> n >> m >> q;
    while (m --) {
        int x, y;
        cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    spfa();
    if (g[1].size() == 0) {
        while (q --) puts("No");
    }
    else {
        while (q --) {
            int a, L;
            cin >> a >> L;
            puts( dis[a][L%2] != -1 && dis[a][L%2] <= L ? "Yes" : "No" );
        }
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM