1. 程式人生 > 其它 >Educational Codeforces Round 103 (Rated for Div. 2)部分題解(A,B,C,D)

Educational Codeforces Round 103 (Rated for Div. 2)部分題解(A,B,C,D)

技術標籤:找規律dp-動態規劃

oj: CodeForces

敘述採用倒敘

D. Journey(dp)

oj: CodeForces

題意

n + 1 n+1 n+1 個點和 n n n 條邊組成的一條鏈,鏈上的邊只能有一個朝向(向左或者向右)。

有個旅行者從一個點出發後只能順著邊的朝向移動,不過每當他移動一格,所有邊的朝向都會改變(朝左的變成朝右,朝右的變成朝左)。

求出旅行者分別在每一個點出發能夠走到的最多的點數。

題解

定義 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]

i i i 0 0 0 時表示只向左移動,為 1 1

1 時表示只向右移動。

j j j 0 0 0 時表示邊是初始朝向,為 1 1 1 時表示邊的朝向發生了改變。

k k k 表示以第 k k k 個點為起點。

d p dp dp 值表示第 k k k 個點只向左(或者只向右,取決於 i i i )能夠走到的最多的點數。

邊界:

所有的點都不移動,即走過的點只有自己: d p [ i ] [ j ] [ k ] = 1 dp[i][j][k] = 1 dp[i][j][k]=1

轉移:

考慮旅行者只向左移動時(即 i = 0 i=0 i=0 ),當左鄰邊朝向左,旅行者只能在朝向未改變時通過。當左鄰邊朝向右,旅行者只能在朝向改變時通過。

所以有:

if(s[i - 1] == 'L') dp[0][0][i] += dp[0][1][i - 1];
else dp[0][1][i] += dp[0][0][i - 1];

考慮旅行者只向右移動時(即 i = 1 i=1 i=1 ),當右鄰邊朝向左,旅行者只能在朝向改變時通過。當右鄰邊朝向右,旅行者只能在朝向未改變時通過。

所以有:

if(s[i] == 'R') dp[1][0][i] += dp[1][1][i + 1];
else dp[1][1][i] += dp[1][0][i + 1];

因為向左和向右時我們都計算了本身的貢獻,所以第 i i i 位的答案應該是 d p [ 0 ] [ 0 ] [ i ] + d p [ 1 ] [ 0 ] [ i ] dp[0][0][i]+dp[1][0][i]

dp[0][0][i]+dp[1][0][i] -1。

程式碼

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
using namespace std;
typedef long long LL;
const int maxn = 300005;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n;
char s[maxn];
LL dp[2][2][maxn];

void init() {
    _for(j, 2) _for(i, n + 3) dp[j][0][i] = dp[j][1][i] = 0;
}

void sol() {
    init();
    scanf("%s", s + 1);
    _rep(i, 1, n + 1) dp[0][0][i] = dp[0][1][i] = 1;
    _rep(i, 2, n + 1) {
        if(s[i - 1] == 'L') dp[0][0][i] += dp[0][1][i - 1];
        else dp[0][1][i] += dp[0][0][i - 1];
    }
    _rep(i, 1, n + 1) dp[1][0][i] = dp[1][1][i] = 1;
    for(int i = n; i >= 1; --i) {
        if(s[i] == 'R') dp[1][0][i] += dp[1][1][i + 1];
        else dp[1][1][i] += dp[1][0][i + 1];
    }
    _rep(i, 1, n + 1) printf("%lld ", dp[0][0][i] + dp[1][0][i] - 1);
    printf("\n");
}

int main() {
    int T = read();
    _for(i, T) {
        n = read();
        sol();
    }
    return 0;
}

C. Longest Simple Cycle(dp)

oj: CodeForces

題意

給你多幹條鏈,每條鏈有三個屬性a,b,c。

c表示這條鏈有c個點。

a表示這條鏈的第1個點和上一條鏈的第a個點連在一起。

b表示這條鏈的最後一個點和上一條鏈的第b個點連在一起。

如圖所示:

求出圖中的最長簡單環。

如圖:

題解

定義dp[i]為第i條鏈能從前面獲得的最大貢獻(包含第i條鏈連向上一條鏈的兩個邊,不包含自身的長度)。

當我們考慮第i條鏈能形成的最長的環時,第i條鏈本身以及連上上一條鏈的兩個邊一定會被計算在內。剩下的有兩種情況,其一是計算a和b之間的長度形成閉環。所以有:

dp[i] = 2 + abs(b[i] - a[i]);

其二是通過a和b兩端進入更前面的鍊形成閉環。不過需要注意的是當a=b時,我們無法延伸到更前面的鏈,當i為1時,前面也沒有鏈可供延伸。所以有:

if(a[i] != b[i] && i > 1)
    dp[i] = max(dp[i], 2 + dp[i - 1] + a[i] - 1 + c[i - 1] - b[i] - max(0, a[i] - b[i]) * 2);

這兩種情況對後續的連法無任何影響,所以我們取最大值即可。

程式碼

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
typedef long long LL;
const int maxn = 100005;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n;
LL a[maxn], b[maxn], c[maxn];
LL dp[maxn];

void init() {
    _for(i, n) dp[i] = 0;
}

void sol() {
    init();
    _for(i, n) c[i] = read();
    _for(i, n) a[i] = read();
    _for(i, n) b[i] = read();
    LL ans = 0;
    for(int i = 1; i < n; ++i) {
        dp[i] = 2 + abs(b[i] - a[i]);
        if(a[i] != b[i] && i > 1) dp[i] = max(dp[i], 2 + dp[i - 1] + a[i] - 1 + c[i - 1] - b[i] - max(0, a[i] - b[i]) * 2);
        ans = max(ans, c[i] - 1 + dp[i]);
    }
    printf("%lld\n", ans);
}

int main() {
    int T = read();
    _for(i, T) {
        n = read();
        sol();
    }
    return 0;
}

B. Inflation

oj: CodeForces

題意

有一件商品每個月都會漲價,初始價格是 p 0 p_0 p0 ,之後每個月都會漲 p i p_i pi

每次漲價的通貨膨脹係數是 p i ∑ j = 0 i − 1 p j × 100 \frac{p_i}{\sum_{j=0}^{i-1}{p_j}}\times 100 j=0i1pjpi×100

你需要通過增加一些 p i p_i pi 使得每次漲價的通貨膨脹係數都不超過 k k k

題解

考慮到後面的漲價對前面的沒有影響,前面的漲價對後面的有影響,所以我們從後往前列舉。

∑ j = 0 i − 1 p j \sum_{j=0}^{i-1}{p_j} j=0i1pj 統稱為 s u m sum sum 。由於影響 p i p_i pi 的因素只有 s u m sum sum ,而且我們只關心 s u m sum sum 的大小,而不關心具體的分配方式,所以我們利用 p i p_i pi 求出所需的最小的 s u m sum sum ,在和實際有的字首和做差,這個差值就是我們需要在前面補上的最小的漲價數(差值為 0 0 0 或小於 0 0 0 就不補)。

針對每一個 p i p_i pi 都求出一個差值,所有的差值取最大值就是答案。

程式碼

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
typedef long long LL;
const int maxn = 1005;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n, k;
LL a[maxn], d[maxn];

void sol() {
    _for(i, n) a[i] = read();
    d[0] = a[0];
    for(int i = 1; i < n; ++i) d[i] = d[i - 1] + a[i];
    LL ans = 0;
    for(int i = 1; i < n; ++i) {
        ans = max(ans, (a[i] * 100 + k - 1) / k - d[i - 1]);
    }
    printf("%lld\n", ans);
}

int main() {
    int T = read();
    _for(i, T) {
        n = read(), k = read();
        sol();
    }
    return 0;
}

A. K-divisible Sum

oj: CodeForces

題意

把若干個 k k k 分成 n n n 份,每份都不為 0 0 0 ,求出最小的最大塊。

題解

先求出能保證每份都不為 0 0 0 的最小的 k k k 的倍數作為 k k k ,然後判斷 k k k 能否被 n n n 整除即可。

程式碼

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int main() {
    int T = read();
    _for(i, T) {
        LL n = read(), k = read(), t = k;
        if(k % n) k = (n + k - 1) / k * k;
        if(k % n == 0) printf("%d\n", k / n);
        else printf("%d\n", k / n + 1);
    }
    return 0;
}