AtCoder Beginner Contest 124 C,D問題メモ
C - Coloring Colorfully
問題
- 左右一列に N 枚のタイルが並ぶ
- 各タイルの色は長さ N の文字列 S によって与えられ、'0'なら黒、'1'なら白
- いくつかのタイルを塗り替えて、以下の条件を満たしたい
- どの隣り合う2枚のタイルも異なる色
- 塗り替える必要のある最小枚数を求めよ
- 1≤N≤105
解法
最終的な状態は、'010101…
' か '101010…
' しかない。(長さは N )
これが初期状態と違えば塗り替える必要がある。一つずつカウントする。
2種類の最終状態を両方試し、小さい方が答え。
'0', '1'を整数化した上で t
に前のタイルの色が入っているとして、t ^= 1
で0と1を反転できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def solve(init, s): t = init ret = 0 for c in s: if c ! = t: ret + = 1 t ^ = 1 return ret s = input () s = list ( map ( int , s)) print ( min (solve( 0 , s), solve( 1 , s))) |
D - Handstand
問題
- N 個の'0'または'1'が並ぶ
- 最大 K 回、以下の操作が行える
- 任意の区間 [l,r] を選んで、その区間の'0'と'1'を反転させる
- 最終的に、'1' を最大何個並ばせることが出来るか求めよ
解法
こういう「'0'と'1'を区間で反転させることで要素を揃えていく」問題では、反転という操作は「異なる要素の境界を減らす」ことを意味すると考えると上手くいくことが多い。
111001011100010 ~~~~~ 111000100000010 ↑ ↑ こことここの境界が消えてる
今回、操作は K 回まで行え、左右端とも自由に決められるので、最大 2K 個の境界を無くせる。
無くす境界をどのように選ぶのがよいかというと、'1'を連続させるので、
11111 | 000 | 1 | 0000 | 111
こういう風な、'1'から始まる'1'と'0'交互の連続する境界について無くせばよい。
よって、'1'と'0'の連続する要素毎にカウントして、(連長圧縮)
1110001000000100 ↓ 111 000 1 000000 1 00 ↓ [ 3, 3,1, 6,1, 2, 0] ←便宜上、左右端が'0'だった場合は'1'が0個と考えて0を追加しておく
連続する 2K+1 要素の和の中で、最大のものを求めればよいことになる。 ただし両端はもともと '1' だった箇所でなければいけないので、0,2,4,…番目から始まる区間のみ調べる。
K = 2 S = 111 000 1 000000 1 00 11111 00 1 0 [ 3, 3, 1, 6, 1, 2, 5, 2, 1, 1, 0 ] ~~~~~~~~~~~~~~ = 14 ~~~~~~~~~~~~~~ = 15 ←答え ~~~~~~~~~~~~~~ = 11 ~~~~~~~~~~~~~~ = 9
区間の和は、累積和または尺取法を用いれば高速に行える。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
from itertools import accumulate n, k = list ( map ( int , input ().split())) s = input () def solve(): l = [] prev = '1' cnt = 0 for c in s: if prev = = c: cnt + = 1 else : l.append(cnt) cnt = 1 prev = c l.append(cnt) if prev = = '0' : l.append( 0 ) w = 2 * k + 1 if len (l) < = w: return n acc = [ 0 ] + list (accumulate(l)) ans = 0 for i in range ( 0 , len (l) - w + 1 , 2 ): ans = max (ans, acc[i + w] - acc[i]) return ans print (solve()) |