Home AtCoder Beginner Contest 271 C(二分) D(DP) E(DP/最短路) F(meet-in-the-middle)
Post
Cancel

AtCoder Beginner Contest 271 C(二分) D(DP) E(DP/最短路) F(meet-in-the-middle)

D. Flip and Adjust (DP)

题目:

$n$ 张卡片, 正面数字 $a_i$, 背面数字 $b_i$

你来决定每张卡片正面朝上还是反面朝上

但是最后卡片上显示的数字总和必须为 $s$

思路:

定义 $dp[i][j]$: 是否能从 $1, 2, …, i$ 张卡片中得到总和 $j$

转移:

  1. $dp[i + 1][j] = 0$
  2. if $dp[i][j] == 1$

    2.1: if $j + a_i < s$, 则 $dp[i + 1][j + a_i] = 1$

    2.2: if $j + b_i < s$, 则 $dp[i + 1][j + b_i] = 1$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <bits/stdc++.h>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n, s;
    cin >> n >> s;

    vector<int> a(n), b(n);
    for (int i = 0; i < n; i++)
    {
        cin >> a[i] >> b[i];
    }

    vector<vector<bool>> dp(n + 1, vector<bool>(s + 1, false));
    dp[0][0] = true;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j <= s; j++)
        {
            if (dp[i][j])
            {
                if (j + a[i] <= s)
                {
                    dp[i + 1][j + a[i]] = true;
                }
                if (j + b[i] <= s)
                {
                    dp[i + 1][j + b[i]] = true;
                }
            }
        }
    }

    if (dp[n][s])
    {
        cout << "Yes" << endl;
        string ans(n, 'H');
        for (int i = n - 1; i >= 0; i--)
        {
            if (s >= a[i] && dp[i][s - a[i]])
            {
                ans[i] = 'H';
                s -= a[i];
            } else {
                ans[i] = 'T';
                s -= b[i];
            }
        }

        cout << ans << endl;
    } else {
        cout << "No" << endl;
    }

    return 0;
}

E. Subsequence Path (DP)

题目:

现在有 $n$ 个点, $m$ 条带权有向边, 现在我们给定一个序列 $E$ , 我们称从 $1$ 到 $n$ 的一条路径为好的, 当且仅当他经过的边的编号, 是 $E$ 的子序列, 现在请找出最短的好的路径, 如果没有则输出 $-1$

思路:

因为给定了固定的序列, 那么我们对于一个路径其实就是选与不选两种选择, 我们可以dp递推一下, 用这条路径更新dist[v]即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <bits/stdc++.h>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n, m, k;
    cin >> n >> m >> k;
    vector<int> a(m), b(m), c(m);
    for (int i = 0; i < m; i++)
    {
        cin >> a[i] >> b[i] >> c[i];
        a[i] -= 1;
        b[i] -= 1;
    }

    vector<long long> dist(n, 1e18);
    dist[0] = 0;
    while (k--)
    {
        int e;
        cin >> e;
        e--;

        if (dist[b[e]] > dist[a[e]] + c[e])
        {
            dist[b[e]] = dist[a[e]] + c[e];
        }
    }

    if (dist[n - 1] == 1e18)
    {
        cout << -1 << endl;
    } else {
        cout << dist[n - 1] << endl;
    }

    return 0;
}

F. XOR on Grid Path (meet-in-the-middle)

题目:

给定一个 $n * n$ 的矩阵, 每个点上都有一个权值, 问从左上角出发到右下角, 并且路径权值异或和为0的方案数

思路:

  1. 正常DP: 时间复杂度$O(2^{2n})$
  2. meet-in-the-middle(双向广搜): 从 $(1, 1)$ 向对角线搜, $(n, n)$ 也向对角线搜, 最后合并结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <bits/stdc++.h>

using namespace std;

int main()
{
    int n;
    cin >> n;

    vector<vector<int>> a(n, vector<int>(n, 0));
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < n; ++j)
        {
            cin >> a[i][j];
        }
    }

    vector<vector<vector<int>>> src(n, vector<vector<int>>(n, vector<int>()));
    src[0][0].push_back(a[0][0]);
    
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < n; ++j)
        {
            if (i + j >= n)
            {
                continue;
            }
            if (i > 0)
            {
                for (const int x : src[i - 1][j])
                {
                    src[i][j].push_back(x ^ a[i][j]);
                }
            }
            if (j > 0)
            {
                for (const int x : src[i][j - 1])
                {
                    src[i][j].push_back(x ^ a[i][j]);
                }
            }
        }
    }

    vector<vector<vector<int>>> dst(n, vector<vector<int>>(n, vector<int>()));
    dst[n - 1][n - 1].push_back(a[n - 1][n - 1]);

    for (int i = n - 1; i >= 0; --i)
    {
        for (int j = n - 1; j >= 0; --j)
        {
            if (i + j < n - 1)
            {
                continue;
            }
            if (i + 1 < n)
            {
                for (const int x : dst[i + 1][j])
                {
                    dst[i][j].push_back(x ^ a[i][j]);
                }
            }
            if (j + 1 < n)
            {
                for (const int x : dst[i][j + 1])
                {
                    dst[i][j].push_back(x ^ a[i][j]);
                }
            }
        }
    }

    long long ans = 0;
    for (int i = 0; i < n; ++i)
    {
        const int j = n - i - 1;
        sort(begin(dst[i][j]), end(dst[i][j]));

        for (const int x : src[i][j])
        {
            const int val = a[i][j] ^ x;
            ans += upper_bound(begin(dst[i][j]), end(dst[i][j]), val) - lower_bound(begin(dst[i][j]), end(dst[i][j]), val);
        }
    }

    cout << ans << '\n';

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
unordered_map<int, int> f[N][N];
int a[N][N], ans, n, m;

void dfs1(int x, int y, int v) {
    if(x + y == n) {
        f[x][y][v] ++ ;
        return ;
    }
    else {
        if(x + y < n) dfs1(x + 1, y, v ^ a[x + 1][y]);
        if(x + y < n) dfs1(x, y + 1, v ^ a[x][y + 1]);
    }
}

void dfs2(int x, int y, int v) {
    if(x + y == n) {
        ans += f[x][y][v ^ a[x][y]];
        return ;
    }
    else {
        if(x + y > n) dfs2(x - 1, y, v ^ a[x - 1][y]);
        if(x + y > n) dfs2(x, y - 1, v ^ a[x][y - 1]); 
    }
}

signed main() {
    cin >> n;
    for(int i = 1; i <= n ; i ++ )
        for(int j = 1; j <= n ; j ++ ) 
            cin >> a[i][j];

    dfs1(1, 1, a[1][1]);
    dfs2(n, n, a[n][n]);

    cout << ans << endl;
}
This post is licensed under CC BY 4.0 by the author.

AtCoder Beginner Contest 270 D(dp) E(二分)

Meet in the middle