1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AtCoder Beginner Contest 055

Last updated at Posted at 2017-02-18

2017年2月18日のAtCoderの記録。言語はRubyです。

問題はこちら:AtCoder Beginner Contest 055
私の回答一覧はこちら:自分の結果

A問題

特に言うことなし。問題文そのまま。

n = gets().to_i
puts n*800-n/15*200

B問題

$N!$ を $10^9+7$ で割った余りを求める問題。最後に割ろうとすると数が大きくなりすぎるので良くない。1つ掛けるごとに剰余を出せばよい。

i = gets.to_i
ans = 1
1.upto(i) do |k|
	ans = ans * k % (1000000000+7)
end
puts ans

C問題

書いたコードの量は、前回のC問題(深さ優先探索)に比べて随分少なくなった。頭を使えば書くべきコードを減らせるタイプの問題である。

全数探索:cを組み合わせてSにする回数さえ決まれば最大数は決まる。したがってcをSにする回数に関して、全ての可能性を調べて最大値を取れば良い。しかし、cの個数の最大が$10^{12}$なので、この方法では時間オーバーする。
二分探索:これもダメ。計算量を減らせそうだが、「cをSにする操作」をi回行ったときのSccの個数をf(i)とすると、f(i)は山型の関数になる。したがって最大値を二分探索で求めることは不可能。

正解の解法は以下のようになる。
頭の中で一旦、Sを全て2つのcにバラす。最初にn個のsとm個のcがあったので、全部で(2*m+n)個のcができる。この状態からSccを作るには、4つずつ組にしていってからcを組み合わせてSにすればよい。(※1)
しかし実際には「Sを全て2つのcにバラす操作」はできない。そのため最初にSが多くてcが少ないと、(※1)の数よりも少なくなる。(たとえばSが10個でcが2個の場合、Sccは1組しかできない)その場合はcの数が制約条件になるので、m/2が答え。

n,m = gets.split(" ").map{|v|
	v.to_i
}
 
all = n * 2 + m
if m >= all/4*2 then
	puts all / 4
else
	puts m/2
end

短くしようとすると2行までは縮められる(下記のコードから変数allを削除すれば良い)。ワンライナーでできるかは分からない。しかし……ここまで簡潔に書ける問題がC問題で良いのかなぁ。

n,m = gets.split.map &:to_i
all = n * 2 + m
puts m >= all/4*2 ? all / 4 : m/2

D問題

ある点の動物と両隣の動物がわかれば、その点の解答が分かる。
自身と両隣のうちどれか1つが(羊から狼に/狼から羊に)入れ替わると、回答の○×も入れ替わる。これより、自身と両隣の3匹のうち、羊の匹数の偶奇によって回答が決まる(全員羊なら○なので、羊が奇数なら○、偶数なら×になる)。論理演算子としてはXOR を使えば良い。
1,2番の動物を仮に決めると、2番の発言から3番の動物がきまり、3番の発言から4番の動物がきまり……最後にN番の動物が決まる。最後にN番と1番の発言に合致するかをチェックすれば良い。(実際のコードでは代わりに、1番の動物と2番の動物が最初に仮に決めたものと合致するかを調べている。直前のコードをそのまま利用できるからである。)
仮に決める方法は4通りあるので、全部試してダメだったら-1を出力。

def check(ans, n, s)
	# 2番からn-1番の回答に合致させる
	1.upto(n-2) do |i|
		temp = (ans[i-1] == "S") ^ (ans[i] == "S") ^ (s[i] == "o") 
		ans[i+1] = temp ? "S" : "W"
	end
 
	temp = (ans[n-2] == "S") ^ (ans[n-1] == "S") ^ (s[n-1] == "o")
	ans_zero = temp ? "S" : "W"
 
	temp = (ans[n-1] == "S") ^ (ans[0] == "S") ^ (s[0] == "o")
	ans_one = temp ? "S" : "W"
 
	if ans_zero == ans[0] && ans_one == ans[1] then
		return true
	else
		return false
	end
end
 
 
n = gets.to_i
s = gets.chomp
 
ans = []
ans[0] = "S"
ans[1] = "S"
if check(ans, n, s) 
	puts ans.join
	exit
end
 
ans = []
ans[0] = "S"
ans[1] = "W"
if check(ans, n, s) 
	puts ans.join
	exit
end
 
ans = []
ans[0] = "W"
ans[1] = "S"
if check(ans, n, s) 
	puts ans.join
	exit
end
 
ans = []
ans[0] = "W"
ans[1] = "W"
if check(ans, n, s) 
	puts ans.join
	exit
end
 
puts -1

コードの後半に関しては「ループ使えばスマートに書けそうだけど時間かかるし、いいやベタに書いちゃえ」という方針。
ans = ["W", "S"]みたいに書けば良かったな。

何かいい方法が無いかと調べてみたら、repeated_permutationというメソッドが今回の目的にぴったりだった。重複を許した順列、というと仰々しいが、n個の候補から重複を許してm個並べた、n^m個の全通りの並べ方を返すものである。公式リファレンスも参照。
このメソッドを使うと、このように簡潔に書ける。

["S", "W"].repeated_permutation(2) { |ans|
	if check(ans, n, s)
		puts ans.join
		exit
	end
}
puts -1

以上。レートが1113まで上がったので、上手く行けばもうすぐABCは卒業かな。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?