【解題報告】Codeforces Round #374 (Div. 2)

題目鏈接


A.One-dimensional Japanese Crossword(Codeforces 721A)

思路

本題是讓我們統計題給字符串中連續的‘ B 塊’的塊數,及每塊的長度。可以將字符串中的‘ W ’全部改成空格,再製成字符串流,然後從字符串流中邊輸入代表‘ B 塊’的子串邊統計子串長度就能得到答案了。

代碼

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

int n;
string s;
vector <int> ans;

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> s;
    for(int i = 0; i < s.size(); i++) {
        if(s[i] == 'W') {
            s[i] = ' ';
        }
    }
    stringstream ss(s);
    while(ss >> s) {
        ans.push_back(s.size());
    }
    cout << ans.size() << endl;
    for(int i = 0; i < ans.size(); i++) {
        cout << ans[i] << ' ';
    }
    return 0;
}

B. Passwords(Codeforces 721B)

思路

由於密碼是按照長度順序輸入,而同長度的密碼隨機輸入。
因此最好的情況是輸入完長度小於真實密碼的密碼之後(設其共有 sum 個)立即輸入到真實密碼。時間 = 罰時 + 真實時間 =sum/k×5+sum+1
最壞的情況是輸入完長度小於真實密碼的密碼之後將與真實密碼長度相同的其它密碼(設其有 num1 個)全部輸完才輸入真實密碼,時間 = 罰時 + 真實時間 =(sum+num1)/k×5+sum+num

代碼

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

const int maxn = 200;
int n, k, a, b, len, sum, num, cnt[maxn];
string s;

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> k;
    for(int i = 0; i < n; i++) {
        cin >> s;
        cnt[s.size()]++;
    }
    cin >> s;
    len = s.size();
    for(int i = 1; i < len; i++) {
        sum += cnt[i];
    }
    num = cnt[len];
    a = sum / k * 5 + sum + 1;
    b = (sum + num - 1) / k * 5 + sum + num;
    cout << a << ' ' << b << endl;
    return 0;
}

C.Journey(Codeforces 721C)

思路

因爲本題的圖是個有向無環聯通圖,又需要在這個圖上求某個最大值。所以自然想到用動態規劃來解決。但是因爲動態規劃需要一個“序”,而這個圖是無序的,因此想到從點 1 開始對這張圖做一個拓撲排序,然後做動態規劃。然而在拓撲排序的過程中動態規劃可以同時進行。
定義 d[i][j] 爲從 i 走到 n ,在經過 j 個節點的情況下花費的最小時間。其中 d[u][j]=min(d[u][j],d[v][j1]+w) ,其中 v 爲拓撲序中 u 的下一個點,另外 w 爲邊權(時間)。對於一個給定的 ujv 要遍取所有的情況才能更新 d[u][j]
爲了按照主人公行走的順序輸出點的編號,在更新 d[u][j] 的時候要記錄在此情況下 u 的下一個節點,存儲在 x[u][j] 中。爲了將時間限制在 T 秒內,需要將 d 數組的所有值初始化爲 T+1 ,再令 d[n][1]=0
在這個算法下,所有的點都只被訪問一次,被訪問的點都只會做 O(n) 的動態規劃,所有的邊也只被訪問一次,因此時間複雜度爲 O(n2+m)

代碼


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

typedef pair <int, int> p;
const int maxn = 5010;
bool vis[maxn];
int n, m, T, u, v, w, ans, cur, x[maxn][maxn], d[maxn][maxn];
vector <p> G[maxn];

// 在拓撲排序的同時動態規劃
void dfs(int u) {
    vis[u] = true;
    for(int i = 0; i < G[u].size(); i++) {
        p& node = G[u][i];
        int v = node.first;
        int w = node.second;
        if(vis[v] == false) {
            dfs(v);
        }
        for(int j = 2; j <= n; j++) {
            if(d[u][j] > d[v][j-1] + w) {
                // 更新數組
                d[u][j] = d[v][j-1] + w;
                // 記錄路徑
                x[u][j] = v;
            }
        }
    }
}

int main() {
    scanf("%d%d%d", &n, &m, &T);
    while(m--) {
        scanf("%d%d%d", &u, &v, &w);
        G[u].push_back(p(v, w));
    }
    // 初始化
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            d[i][j] = T + 1;
        }
    }
    d[n][1] = 0;
    // 拓撲排序
    dfs(1);
    for(int j = n; j >= 1; j--) {
        if(d[1][j] <= T) {
            ans = j;
            break;
        }
    }
    printf("%d\n", ans);
    // 按行走順序輸出
    cur = 1;
    for(int i = ans; i >= 1; i--) {
        printf("%d ", cur);
        cur = x[cur][i];
    }
    return 0;
}

D.Maxim and Array(Codeforces 721D)

思路

序列的積由兩個值唯一決定,一個是積的負號,另一個是積的絕對值。
當積的符號爲正的時候,只要將序列中絕對值最小的元素不斷減小(直到積的符號爲負)就能夠減小積的大小。
當積的符號爲負的時候,只要將積的絕對值增大就能減小積的大小。爲了讓積的絕對值增大,可以找到序列中絕對值最小的元素,增加其絕對值即可。
那麼我們可以在模擬k減小的同時維護積的符號 sign ,並根據其值來執行相應的操作。爲了動態獲得當前狀態下序列中絕對值最小的元素。我們需要用一個堆來維護序列中每個元素的絕對值。這樣總的複雜度是 O(nlogn)

代碼

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

typedef long long ll;
typedef pair <ll, int> p;
const int maxn = 2e5 + 5;
// sign的初始值爲0,表示積爲非負數
int n, k, x, v, d, sign, flag;
ll a[maxn];
priority_queue < p, vector<p>, greater<p> > pq;

int main() {
    scanf("%d%d%d", &n, &k, &x);
    for(int i = 0; i < n; i++) {
        scanf("%I64d", &a[i]);
        // 將絕對值插入堆中,附加位置信息
        pq.push(p(abs(a[i]), i));
        // 當a[i]爲負數時修改積的符號
        sign ^= (a[i] < 0);
    }
    // 模擬數組的減小過程
    while(k--) {
        p tmp = pq.top();
        pq.pop();
        v = tmp.first;
        d = tmp.second;
        // 記錄修改之前的符號
        flag = (a[d] < 0);
        // 修改序列的元素的值
        a[d] += (sign ^ flag ? 1 : -1) * x;
        // 若符號改變則修改sign
        sign ^= flag ^ (a[d] < 0);
        pq.push(p(abs(a[d]), d));
    }
    for(int i = 0; i < n; i++) {
        printf("%I64d ", a[i]);
    }
    return 0;
}

(其它題目略)

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