ほぼ静的な計画法

競技プログラミングで解いた問題の解法とコードを晒していくページ。ややマイナーなC♯。

AOJ 0053 (Sum of Prime Numbers : 素数の和)

【問題】

https://onlinejudge.u-aizu.ac.jp/problems/0053

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0053

【概要】

n番目の素数までの素数の和を求める問題。

【解説】

各データセットに対してO(1)で返せるようにするために、あらかじめ累積和の配列(i番目にi番目までの素数の合計を格納した配列)を用意しておく。

素数の判定は、整数を順にエラトステネスの篩を利用して判定すればよい。判定の結果、素数であれば、その素数自身と配列の前の要素の合計を配列に格納していくことで、累積和の配列を作ることができる。

【コード】

AOJ 0052 (Factorial II : 階乗 II)

【問題】

https://onlinejudge.u-aizu.ac.jp/problems/0052

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0052

【解説】

n!の末尾の0の数を求める問題。nが最大で20000となるため、実際に階乗した末尾をカウントしようとするとlong型でも収まらないため、別の方針を立てる必要がある。

そこで、求めようとする「末尾の0の数」を言い換えると、これは、n!の因数における10の数である。よって、nを素因数分解した場合の2の数と5の数のうち、少ない方の数が10の数であるが、明らかに5の倍数の方が少ないため、nまでの数を素因数分解した場合の5の数を数えていけばよい。

25,125等は、素因数に5をそれぞれ複数含むため、数え漏れないように注意。

【コード】

AOJ 0051 (Differential II : 整数の差)

【問題】

https://onlinejudge.u-aizu.ac.jp/problems/0051

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0051

【解説】

最大の整数は、貪欲的に上の桁から大きな整数をとっていく場合となるため、文字列の降順にソートすればよい。また、最小の整数については、その反転となるため、逆順にソートすればよい。あとは最大ー最小を求めて出力する。

【コード】

AOJ 0041 (Expression : 式)

【問題】

https://onlinejudge.u-aizu.ac.jp/problems/0041

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0041

【解説】

整数a,b,c,dと演算子+,-,*および()を使用して、10を作ることのできる式を求める問題。肝は全てのパターンをもれなく重複なく数え上げることができるかどうかとなる。

まず、()による優先順位付けは取り漏らす可能性があるため、逆ポーランド記法にて考えることとした。数を4つ(〇)、演算子を3つ(×)使用する場合の逆ポーランド記法として考えられるパータンは、

  1. 〇〇×〇〇××
  2. 〇〇×〇×〇×
  3. 〇〇〇×〇××
  4. 〇〇〇××〇×
  5. 〇〇〇〇×××

の5パータンのみである。なぜなら、演算子が出現する場合には、スタックに数値が2つ以上格納されている必要があるため、必ず使用した演算子の数より多く数値が格納されている必要がある。そのため、初めの2つは「〇〇」から始まる必要がある。また、処理の最後は演算となるため、末尾は「×」となる必要がある。よって3文字目~6文字目に〇2個、×2個を入れる組み合わせの6パターンのうち「××○○」となるものを除く5パターンが候補となる。(「××○○」だと「〇〇××〇〇×」となり、2つ目の演算子が出現した時点で演算子超過となる。)

あとは、各〇に数値のa,b,c,dの順列を、各×に演算子+,-.*をそれぞれ入れて、逆ポーランド記法の演算を行い、10になるかを全探索していく。順列の求め方については、集合から任意の1要素を取り出し、取り出した要素を除いた集合からさらに任意の1要素を取り出し、取り出した要素を除いた・・・・という処理を再帰的に行っていくことで求めることができる。

ここで、上記の2~5のパターンに着目すると、これらは全て「任意の2数を演算する」⇒「その結果に残りの数のいずれかを演算する」⇒「その結果に残りの数を演算する」という処理となるため、このうち1パーンのみを処理すれば十分である。よって、上記の1と2のパターンについてのみ探索し、判定結果によって0または逆ポーランド記法中置記法に直した文字列を出力すればよい。

【コード】

AOJ 0050 (Apple and Peach : りんごと桃)

【問題】

https://onlinejudge.u-aizu.ac.jp/problems/0050

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0050

【解説】

文字列を置換すればよいが、apple=peachpeach=appleと置換すると、最初に置換したものも次で置換されてしまうため、一時的に初期状態で与えられない文字列("あいうえお"等)を利用して、apple→あいうえお、peachapple、あいうえお→peachのように処理する。

【コード】

AOJ 0049 (Blood Groups : 血液型)

【問題】

https://onlinejudge.u-aizu.ac.jp/problems/0049

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0049

【解説】

与えられる血液型の個数をコレクション等で管理し、カウントしていけばよい。

【コード】

AOJ 0048 (Class : 階級)

【問題】

https://onlinejudge.u-aizu.ac.jp/problems/0048

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0048

【解説】

それぞれの条件に合致するようif文を書いて出力すればよい。

以下のコードでは、各階級の上限値を格納したSortedDictinaryを用意し、与えられた値以上の最初の要素を返すことで実現している。

【コード】