Title

AtCoder Beginner Contest 427(A~E)

AtCoder Beginner Contest 427(A~E)

写在前面

赛时 \(A — E\)

\(A\) : 模拟

\(B\) : 模拟,暴力,前缀和

\(C\) : \(dfs\)/状态压缩,二分图概念,思维

\(D\) : 博弈论(\(P/N\)态)

\(E\) : 思维,\(BFS\)

A - ABC -> AC

思路

按题意模拟就好了

代码

#include <bits/stdc++.h>
#define endl '\n'
// #define int long long


void solve()
{
    std::string s; std::cin >> s;
    int tar = s.size() / 2;
    for (int i = 0; i < (int)s.size(); i++)
    {
        if (i == tar) continue;
        std::cout << s[i];
    }
}

signed main()
{
    std::ios::sync_with_stdio(false);
    std::cout.tie(nullptr); std::cin.tie(nullptr);
    int _t = 1;
    // std::cin >> _t;
    while(_t--) solve();
    return 0;
}

B - Sum of Digits Sequence

思路

\(n\)很小,暴力计算即可

可以维护一个\(f\)数组的前缀和

这个过程就是,对于第\(i\)个数,此时我们应该是已经知道了前\(i - 1\)个元素的\(f\)前缀和
那么此时就可以这样处理

\[ A_i = pref_{i - 1} \]

\[ pref_i = pref_{i - 1} + f[A_i] \]

而至于\(f[A_i]\)我们暴力求解就好

代码

#include <bits/stdc++.h>
#define endl '\n'
// #define int long long

int f[110];
int a[110];
int pre[110];
void solve()
{
    f[0] = 1; f[1] = 1; f[2] = 2; f[3] = 4; f[4] = 8;
    a[0] = 1; a[1] = 1; a[2] = 2; a[3] = 4; a[4] = 8;
    int n = 0; std::cin >> n;
    if (n <= 4)
    {
        std::cout << a[n] << endl;
        return;
    }
    pre[0] = f[0];
    for (int i = 1; i <= 4; i++) pre[i] = pre[i - 1] + f[i];
    for (int i = 5; i <= n; i++)
    {
        a[i] = pre[i - 1];
        int tmp = a[i]; int val = 0;
        while(tmp)
        {
            val += tmp % 10;
            tmp /= 10;
        }
        f[i] = val;
        pre[i] = pre[i - 1] + f[i];
    }
    // std::cout << f[5] << endl;
    std::cout << a[n] << endl;
}

signed main()
{
    std::ios::sync_with_stdio(false);
    std::cout.tie(nullptr); std::cin.tie(nullptr);
    int _t = 1;
    // std::cin >> _t;
    while(_t--) solve();
    return 0;
}

C - Bipartize

思路

首先怎么判断二分图 \(\rightarrow\) 染色法,但是我们这里是要对其操作使之变成二分图,染色法似乎不行. 但是我们可以注意到\(N\)很小,我们可以以\(O(2^n)\)去枚举二分图左集合有哪些元素,剩下的放到右集合。

那么分配完后如何操作使之成为二分图呢,因为二分图只有左右集合点之间的连边,集合内部是不能连边的,所以分别对左右集合内部枚举点对,有边就计数一次,然后每一次枚举左右集合的点时我们就更新最小值即可

这里实现可以\(dfs\),也可以状态压缩

代码

#include <bits/stdc++.h>
#define endl '\n'
// #define int long long
int n, m;
int mat[20][20];
int vis[20];
int ans = INT_MAX;
void dfs(int step)
{
    if (step == n + 1)
    {
        std::vector<int> left, right;
        for (int i = 1; i <= n; i++)
        {
            if (vis[i]) left.push_back(i);
            if (!vis[i]) right.push_back(i);
        } 
        int tmp = 0;
        for (int i = 0; i < (int)left.size(); i++)
        {
            for (int j = i + 1; j < (int)left.size(); j++)
            {
                if (mat[left[i]][left[j]]) tmp++;
            }
        }
        for (int i = 0; i < (int)right.size(); i++)
        {
            for (int j = i + 1; j < (int)right.size(); j++)
            {
                if (mat[right[i]][right[j]]) tmp++;
            }
        }
        ans = std::min(ans, tmp);
        return;
    }
    vis[step] = 1;
    dfs(step + 1);
    vis[step] = 0;
    dfs(step + 1);
}

void solve()
{
    std::cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int u, v; std::cin >> u >> v;
        mat[u][v] = mat[v][u] = 1;
    }
    dfs(1);
    std::cout << ans << endl;
}

signed main()
{
    std::ios::sync_with_stdio(false);
    std::cout.tie(nullptr); std::cin.tie(nullptr);
    int _t = 1;
    // std::cin >> _t;
    while(_t--) solve();
    return 0;
}

D - The Simple Game

思路

对P、N态的理解

  • \(N\)态 (N-position): Next player winning position,即“下一个”玩家(轮到他/她行动的玩家)有必胜策略的位置。可以理解为“必胜态”。

  • \(P\)态 (P-position): Previous player winning position,即“上一个”玩家(刚刚行动完的那个玩家)有必胜策略的位置。对于当前玩家来说,这是一个无论怎么走,对方都有办法赢的局面。可以理解为“必败态”。

  • 无法移动的状态为\(P\)

  • 可以移动到\(P\)的状态为\(N\)

  • 只能移动到\(N\)的状态为\(P\)

这个题目我们代入Alice的视角去考虑
我们考虑游戏结束时,也就是剩余步数为0时,当前落在A,就是必胜态(也就是N态); 当前落在B,就是必败态(也就是P态)
那么当游戏剩余1步时,此时该Bob走了,如果它能走到剩余0步时的P态,那此时Alice必败,那么就是P态; 如果Bob不管怎么走都只能走到N态,那么Alice必胜,那么就是N
\(\dots\)
我们可以按照步数把图分层做\(dp\)

\(dp[i][j]\) : 剩余\(j\)步时,在节点\(i\)\(P\)\(N\)态,\(P\)态为\(1\), \(N态\)\(0\),那么最后我们判断\(dp[1][2k]\)就好了

\(j\)为奇数,也就是该Bob走时

\[ dp[i][j] = \begin{cases} 0 & \exists v|i \space can \space go \space to \space v \space \space \ \ \ \ \ dp[v][j - 1] = 0\\ 1 & \forall v|i \space can \space go \space to \space v \space \space \ \ \ \ \ dp[v][j - 1] = 1 \end{cases} \]

\(j\)为偶数时,也就是该Alice走时

\[ dp[i][j] = \begin{cases} 1 & \exists v|i \space can \space go \space to \space v \space \space \ \ \ \ \ dp[v][j - 1] = 1\\ 0 & \forall v|i \space can \space go \space to \space v \space \space \ \ \ \ \ dp[v][j - 1] = 0 \end{cases} \]

代码

#include <bits/stdc++.h>
#define endl '\n'
// #define int long long
struct Edge
{
    int u, v, nxt;
};
void solve()
{
    int n = 0, m = 0, k = 0;
    std::cin >> n >> m >> k;
    std::vector<char> s(2 * n + 1);
    for (int i = 1; i <= n; i++) std::cin >> s[i];
    std::vector<Edge> g(2 * m + 1);
    std::vector<int> head(3 * n + 1, 0);
    int tot = 1;
    auto Add = [&](int u, int v) -> void
    {
        g[++tot] = {u, v, head[u]};
        head[u] = tot;
    };
    for (int i = 1; i <= m; i++)
    {
        int tu, tv; std::cin >> tu >> tv;
        Add(tu, tv);
    }
    std::vector<std::vector<int>> dp(n + 1, std::vector<int>(2 * k + 1, 0));
    for (int i = 1; i <= n; i++)
    {
        if (s[i] == 'A') dp[i][0] = 1;
    }
    for (int i = 1; i <= 2 * k; i++)
    {
        for (int i = 1; i <= 2 * k; i++)
        {
            if (i % 2 != 0) 
            {
                for (int j = 1; j <= n; j++) 
                {
                    bool ok = true;
                    for (int u = head[j]; u; u = g[u].nxt)
                    {
                        int v = g[u].v;
                        if (dp[v][i - 1] == 0) 
                        {
                            ok = false;
                            break;
                        }
                    }
                    if (ok) dp[j][i] = 1;
                }
            }
            else 
            {
                for (int j = 1; j <= n; j++) 
                {
                    bool ok = false;
                    for (int u = head[j]; u; u = g[u].nxt)
                    {
                        int v = g[u].v;
                        if (dp[v][i - 1] == 1) 
                        {
                            ok = true;
                            break;
                        }
                    }
                    if (ok) dp[j][i] = 1;
                }
            }
        }
    }
    if (dp[1][2 * k] == 1)
    {
        std::cout << "Alice" << endl;
    }
    else
    {
        std::cout << "Bob" << endl;
    }
}

signed main()
{
    std::ios::sync_with_stdio(false);
    std::cout.tie(nullptr); std::cin.tie(nullptr);
    int _t = 1;
    std::cin >> _t;
    while(_t--) solve();
    return 0;
}

E - Wind Cleaning

思路

因为数据很小,我们可以把所有垃圾的位置视为一个状态,但是有可能因为遍历顺序不同导致可能有相同的垃圾位置集合,最后的状态表示不一样,怎么办呢? \(\rightarrow\) 排序,按照一个统一的标准来就好了

接下来的流程就和\(bfs\)一样了

  • 我们用一个队列来记录垃圾状态移动的步数
  • 用一个set记录有没有访问过一个状态
  • 每次先从队列中取出一个状态,然后将这个状态出队,如果这个状态已经遍历过,\(continue\)掉继续遍历,记这个状态为cur
  • 如果cur状态已经合法,结束输出答案
  • 因为所有垃圾都要移动,所以我们按照方向来遍历,对每一个方向移动后的新状态记为nxt
    • 检查状态合法性,不合法立即放弃这个方向
    • 如果有垃圾超出了边界,不在加入后续状态中
  • 队列为空时还没有找到答案就输出-1

代码

#include <bits/stdc++.h>
#define endl '\n'
// #define int long long
const int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
std::vector<std::pair<int, int>> trash;
std::queue<std::pair<std::vector<std::pair<int, int>>, int>> q;
std::set<std::vector<std::pair<int, int>>> vis;
int tx, ty;
int h, w;

void solve()
{
    std::cin >> h >> w;
    for (int i = 1; i <= h; i++)
    {
        for (int j = 1; j <= w; j++)
        {
            char tmp; std::cin >> tmp;
            if (tmp == 'T') {tx = i; ty = j;}
            if (tmp == '#') trash.push_back({i, j});
        }
    }
    std::sort(trash.begin(), trash.end());
    q.push({trash, 0});
    while(!q.empty())
    {
        auto [arr, dis] = q.front(); q.pop();
        if (vis.find(arr) != vis.end()) continue;
        if (arr.empty()) {std::cout << dis << endl; return;}
        vis.insert(arr);
        for (int i = 0; i < 4; i++)
        {
            std::vector<std::pair<int, int>> nxt;
            bool ok = true;
            for (auto p : arr)
            {
                int nx = p.first + dir[i][0];
                int ny = p.second + dir[i][1];
                if (nx == tx && ny == ty)
                {
                    ok = false;
                    break;
                }
                if (nx < 1 || nx > h || ny < 1 || ny > w) continue;
                nxt.push_back({nx, ny});
            }
            if (ok == false) continue;
            std::sort(nxt.begin(), nxt.end());
            q.push({nxt, dis + 1});
        }
    }
    std::cout << -1 << endl;
}

signed main()
{
    std::ios::sync_with_stdio(false);
    std::cout.tie(nullptr); std::cin.tie(nullptr);
    int _t = 1;
    // std::cin >> _t;
    while(_t--) solve();
    return 0;
}
posted @ 2025-10-12 10:40  栗悟饭与龟功気波  阅读(30)  评论(0)    收藏  举报