はじめに
AtCoder Beginner Contest 291の復習記事です。
記事におけるScanner
クラスは、自作の入力クラスです。
コンテスト
https://atcoder.jp/contests/abc291
問題A
S[i]
が大文字かどうかはchar.IsUpper
で調べることができ、そのi
の値を出力します。
public static void Solve()
{
var S = Scanner.Scan<string>();
for (var i = 0; i < S.Length; i++)
{
if (char.IsUpper(S[i]))
{
Console.WriteLine(i + 1);
return;
}
}
}
問題B
X
をソートし、先頭N
人と末尾N
人を除いた3N
人の平均を求めます。
public static void Solve()
{
var N = Scanner.Scan<int>();
var X = Scanner.ScanEnumerable<long>().ToArray();
Array.Sort(X);
var s = X.Skip(N).Take(3 * N).Sum();
var answer = (double)s / (3 * N);
Console.WriteLine(answer);
}
問題C
既に訪れた座標を管理しながら現在の座標を遷移させることで、既に訪れたことがあるかを求めます。
訪れた座標のペアをHashSet
などのデータ構造で管理することで、時間計算量O(1)
で現在の座標が訪れたことがあるかを調べることができます。
public static void Solve()
{
var N = Scanner.Scan<int>();
var S = Scanner.Scan<string>();
var set = new HashSet<(int, int)>();
var x = 0;
var y = 0;
set.Add((x, y));
foreach (var c in S)
{
var dx = 0;
var dy = 0;
if (c == 'R') dx = 1;
if (c == 'L') dx = -1;
if (c == 'U') dy = 1;
if (c == 'D') dy = -1;
x += dx;
y += dy;
if (set.Contains((x, y)))
{
Console.WriteLine("Yes");
return;
}
set.Add((x, y));
}
Console.WriteLine("No");
}
問題D
次のような動的計画法を解きます。
dp[i,j] := i番目のカードまで見たとき、i番目のカードがj(表,裏)のとき条件を満たすものの数
遷移としては次のようになります。
i=1のとき、
dp[1,0] = 1
dp[1,1] = 1
i>1のとき、
dp[i,0] += dp[i-1,0] (A[i]!=A[i-1])
dp[i,0] += dp[i-1,1] (A[i]!=B[i-1])
dp[i,1] += dp[i-1,0] (B[i]!=A[i-1])
dp[i,1] += dp[i-1,1] (B[i]!=B[i-1])
答えは、dp[N,0]+dp[N,1]
となります。
public static void Solve()
{
var N = Scanner.Scan<int>();
var A = new int[N];
var B = new int[N];
for (var i = 0; i < N; i++)
{
var (a, b) = Scanner.Scan<int, int>();
A[i] = a;
B[i] = b;
}
var dp = new mint[N, 2];
dp[0, 0] = dp[0, 1] = 1;
for (var i = 1; i < N; i++)
{
if (A[i] != A[i - 1]) dp[i, 0] += dp[i - 1, 0];
if (A[i] != B[i - 1]) dp[i, 0] += dp[i - 1, 1];
if (B[i] != A[i - 1]) dp[i, 1] += dp[i - 1, 0];
if (B[i] != B[i - 1]) dp[i, 1] += dp[i - 1, 1];
}
var answer = dp[N - 1, 0] + dp[N - 1, 1];
Console.WriteLine(answer);
}
問題E
各整数の組について頂点X
から頂点Y
の有効辺としたグラフを考えたとき、グラフをトポロジカルソートすることができ、かつ始点と終点が一意に定まるかが条件となります。
始点が一意に定まるかは、頂点の入次数が0
の頂点数が1
つであることで判定できます。
同様に、終点が一意に定まるかは頂点の出次数が0
の頂点数が1
つであることで判定できます。
誤答でした。
各整数の組について頂点X
から頂点Y
の有効辺としたグラフを考えたとき、グラフをトポロジカルソートすることができ、頂点の遷移が一意であることが条件となります。
public static void Solve()
{
var (N, M) = Scanner.Scan<int, int>();
var G = new List<int>[N].Select(x => new List<int>()).ToArray();
var E = new HashSet<(int, int)>();
var deg = new int[N];
for (var i = 0; i < M; i++)
{
var (x, y) = Scanner.Scan<int, int>();
x--; y--;
if (E.Contains((x, y))) continue;
E.Add((x, y));
G[x].Add(y);
deg[y]++;
}
var queue = new Queue<int>();
for (var i = 0; i < N; i++)
{
if (deg[i] == 0) queue.Enqueue(i);
}
var result = new int[N];
var idx = 0;
while (queue.Count > 0)
{
if (queue.Count > 1)
{
Console.WriteLine("No");
return;
}
var u = queue.Dequeue();
foreach (var v in G[u])
{
deg[v]--;
if (deg[v] == 0) queue.Enqueue(v);
}
result[idx++] = u;
}
if (idx != N)
{
Console.WriteLine("No");
return;
}
Console.WriteLine("Yes");
var answer = new int[N];
for (var i = 0; i < N; i++)
{
answer[result[i]] = i + 1;
}
Console.WriteLine(string.Join(" ", answer));
}
問題F
dp1[i]
を都市1
から都市i
までにかかる最小の移動回数、dpN[j]
を都市j
から都市N
までにかかる最小の移動回数としたとき、dp1[u]+dpN[v]+1 (1<=u<k<v<=N,v<=u+M)
の最小値が都市k
を通らずに都市1
から都市N
への最小の移動回数となります。
public static void Solve()
{
var (N, M) = Scanner.Scan<int, int>();
var S = new string[N];
for (var i = 0; i < N; i++)
{
S[i] = Scanner.Scan<string>();
}
var dpS = new long[N];
var dpT = new long[N];
const long Inf = (long)1e18;
Array.Fill(dpS, Inf);
Array.Fill(dpT, Inf);
dpS[0] = dpT[N - 1] = 0;
for (var v = 1; v < N; v++)
{
for (var u = Math.Max(v - M, 0); u < v; u++)
{
if (S[u][v - u - 1] == '1') dpS[v] = Math.Min(dpS[v], dpS[u] + 1);
}
}
for (var v = N - 1; v > 0; v--)
{
for (var u = Math.Max(v - M, 0); u < v; u++)
{
if (S[u][v - u - 1] == '1') dpT[u] = Math.Min(dpT[u], dpT[v] + 1);
}
}
var answers = new List<long>(N - 2);
for (var k = 1; k < N - 1; k++)
{
var answer = Inf;
for (var u = Math.Max(k - M, 0); u < k; u++)
{
for (var v = k + 1; v < Math.Min(u + M + 1, N); v++)
{
if (S[u][v - u - 1] == '1') answer = Math.Min(answer, dpS[u] + dpT[v] + 1);
}
}
answers.Add(answer == Inf ? -1 : answer);
}
Console.WriteLine(string.Join(" ", answers));
}