1
1

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 Regular Contest 065

Posted at

2016年12月10日のAtCoderの記録。言語はRubyです。
問題はこちら:AtCoder Regular Contest 065
私の回答一覧はこちら:自分の結果

前回ABC048を余裕を持って完答できたので、「こりゃ次回からはARCのほうに参加したほうが良いな」と思ったのですが、まさかの正解無しという結果でした。苦いデビュー戦というやつですな。

C問題

'dream' 'dreamer' 'erase' 'eraser' の4つを好きな数だけ好きな順番で並べた文字列、に当てはまるか否かを判定する問題。

dreamer... という文字列があったとしても dream / erase...なのかdreamer / ...なのか判定できない。ちょこちょことif判定すれば何とかなりそうだがあんまりスマートじゃない。
そうだ、再帰を使おう。と考えたのが間違いの元であったようだ。

$target = gets.chomp
$frac = ["dream" , "dreamer", "erase", "eraser" ]
 
def addstr(s)
	$frac. each do |frac|
		s1 = s + frac
		if s1 == $target then
			puts "YES"
			exit
		elsif s1 == $target[0 , s1.length] then
			addstr(s1)
		end
	end
	
end
 
addstr("")
puts "NO"

アルゴリズム的には問題ない。addstrで次々に文字列を付け加えていって、入力文字列に一致すれば"YES"を出力してすぐに終了。最後までやっても一致しなければ、"NO"と出力する。
計算量は問題ないと判断した。一般には、文字列の候補が4つに枝分かれ→それぞれがまた4つに枝分かれ→……で4^nの指数時間かかってしまう。この問題の場合、不正解のものは枝分かれするとすぐに棄却されるので、それ以上先に進まない。問題の性質上、枝分かれは膨大にならないので、問題にはならないと考えた。
で、結果がRuntime Error。
それぞれの関数で文字列変数を使うから(メモリが制限オーバーして)アカンのかなと思い、変数をグローバル領域に出したりしても変わらず。

手元のRubyでやってみると、入力が1万文字も行かないあたりで

stack level too deep (SystemStackError)

と出てしまった。
入力の最大長は10万文字なので、このままではダメ。

ABC036 D問題のときも、最大で10万回の再帰をするコードを提出してるけど、このときはAcceptedなんだよなぁ。今回と以前とで何が違うのか分からない。

D問題

頂点集合Vに対して、V上のグラフG1, G2が与えられる。Vの各頂点に対して、G1に関してもG2に関しても連結である頂点の個数を求めよ。という問題。
考えた道筋を記録しておくが、上手く行かなかったアイデアである。
最も単純に思いつくのは、以下のようなアルゴリズムである。(擬似コード)

for each v in V
  幅優先か深さ優先で探索して
  G1 上で連結である頂点の集合を求める
  G2 上で連結である頂点の集合を求める
  2つの頂点集合の共通部分を求める
  共通部分の要素数を出力
end

しかしこれは時間をオーバーする。1回の探索にかかる時間が最悪でO(|E|)である(Eはグラフの辺)。問題条件は、|V|が最大$2×10^5$、|E|が最大$10^5$である。
Pythonで競技プログラミングする時に知っておきたい知識を参照すると、1秒間の計算回数はC++のような処理の速い言語でも$10^8$ぐらい。絶対に間に合わない。

しかし、グラフの連結性は対称性を持つ。例えば頂点2を調べていて、頂点2と連結している成分は2,4,6だけだとわかった場合、頂点2と連結する頂点の要素数は3である。同時に、頂点4と連結している頂点の要素数も3である。というところで計算量を削れそうだ。

for each v in V
  vに関する個数が既に求まっていれば、スキップして次の頂点へ

  幅優先か深さ優先で探索して
  G1 上で連結である頂点の集合を求める
  G2 上で連結である頂点の集合を求める
  2つの頂点集合の共通部分を求める
  共通部分の要素数を、共通部分の全要素に対して出力
end

ここまで考えて実装している途中に時間切れ。
そのあと書き上げたら、小さいテストケースでは通ったが、全体ではTime Limit ExceededやRuntime Errorで撃沈した。

n,k,l = gets.split.map { |i|
	i.to_i
}
 
@neighbor1 = []
n.times do
	@neighbor1 << []
end
 
k.times do
	line = gets.split
	i = line[0].to_i
	j = line[1].to_i
 
	@neighbor1[i-1] << j-1
	@neighbor1[j-1] << i-1
end

@neighbor2 = []
n.times do
	@neighbor2 << []
end
l.times do
	line = gets.split
	i = line[0].to_i
	j = line[1].to_i
 
	@neighbor2[i-1] << j-1
	@neighbor2[j-1] << i-1
end
 
def search1(x, ren)
	for n in @neighbor1[x]
		if ren.include?(n) then
			next
		else
			ren << n
			search1(n, ren)
		end
	end
	return ren
end
def search2(x, ren)
	for n in @neighbor2[x]
		if ren.include?(n) then
			next
		else
			ren << n
			search2(n, ren)
		end
	end
	return ren
end
 
answer = []
n.times do
	answer << 0
end
0.upto(n-1) {|i|
	if answer[i] > 0 
		next # もう求まっているので、次の頂点へ
	end
	# iからの連結成分を取得
	list1 = search1(i, [i])
	list2 = search2(i, [i])
#	p list1, list2
	list = list1 & list2
	l = list.length
	list.each {|t|
		answer[t] = l
	}	
}
answer.each {|t| puts t}

以上。解説見てみたけど、今後どうすれば良いのか分からんな。Union-Findを定石として頭に入れておいたほうが良さそう、くらいかなぁ。

1
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?