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\)前缀和
那么此时就可以这样处理
而至于\(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走时
当\(j\)为偶数时,也就是该Alice走时
代码
#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;
}

浙公网安备 33010602011771号