AISing Programming Contest 2019の解説
A - Bulletin Board
入力:
出力: 貼り出す場所の選び方の場合の数
制約:
writeln(max(0, N - H + 1) * max(0, N - W + 1));
感想: これ最初戸惑った。解説は絵を描きたかっただけ。
B - Contests
入力:
出力: コンテストを実施できる最大の回数
制約:
問題文より、与えられた問題は3つのパターンに分類することができます。
一度のコンテストでは、それぞれの分け方から一問ずつ出題されるため、どんどん消費されていきます。
したがって、一番最初に消費しきってしまうグループの値を出力すればよいです。
ulong a = P.filter!(i => i <= A).array.length; ulong b = P.filter!(i => A+1 <= i && i <= B).array.length; ulong c = P.filter!(i => i >= B + 1).array.length; writeln(min(a, b, c));
感想: なぜか問題を読むのに時間がかかった。風呂上がって髪を乾かした後、もう20秒しかない状態でコンテストに参加したので、10分ぐらい上裸だった。そんな状況でACできたので自分を称えたい(は?)
C - Alternating Path
入力:
出力: 条件を満たすものの個数
制約:
各に対して、文字列は文字'#'と文字'.'だけからなる。
まず、愚直に全探索することを考えてみる(の移動を、すべてのについて考える)と、でとなり、なので間に合いません。
したがって、どうにか計算量を落としたい気持ちになります。
ここで、入力例 1を画像にしてみると、こんな感じになります。
それぞれの'#'から、隣りあった'.'に行けることは問題文からわかります。
問われているのは、'#'から'.'に行ける場合の数です。
例えば、こういうふうにと行くことができます。
こうやって見ると、通うことのできる範囲にある'#'と'.'の個数をかけ算すればよくね?ということに気づきます。これは考えてみるとわかります。
よって、まずは'#'のあるところの座標から探索することにします。
そして、それぞれ問題文の条件を満たすようにDFSで探索し、通うことのできる範囲をしらみつぶしに調べます。その間に計算した座標については訪問済みフラグを立てておき、次は計算をしないように管理します(これで計算量が減ります)。
こうすることで、で計算が終わります。よってTLEを防ぐことができました。
int[] dy = [1, 0, -1, 0], dx = [0, 1, 0, -1]; Pair[] stack; foreach(i, iv; S) foreach(j, jv; iv) { if(jv == '#') stack ~= Pair(i, j); } ulong res; bool[][] done = new bool[][](H, W); //ulong res2; while(stack != []) { auto p = stack.front; stack.popFront; if(done[p.y][p.x]) continue; // 計算量が減るポイント Pair[] stack2; stack2 ~= Pair(p.y, p.x); ulong wh, bl; while(stack2 != []) { auto p2 = stack2.front; stack2.popFront; foreach(k; 0..4) { //周囲4マスを探索します auto ry = p2.y + dy[k]; auto rx = p2.x + dx[k]; if(0 <= ry && ry < H && 0 <= rx && rx < W && S[ry][rx] != S[p2.y][p2.x] && !done[ry][rx]) { if(S[ry][rx] == '.') { res++; wh++; } else bl++; stack2 ~= Pair(ry, rx); done[ry][rx] = true; } } done[p2.y][p2.x] = true; } res += wh * bl; // 通える範囲にある'#'と'.'の個数の積をとり答に加算する } res.writeln;
感想: この発想が出て30分は、結局になってしまう別のコードを書いてしまい、学校の競プロerに抜かされてしまった。悔しい。でも解けたのでオッケーです