クルトンのプログラミング教室

Pythonの使い方やPythonを使った競技プログラミングの解法などを解説しています。

Pythonで理解する蟻本「2-2 Fence Repair(POJ 3253)」(p.49)

この記事は「プログラミングコンテストチャレンジブック第2版」(蟻本)の
「2-2 Fence Repair(POJ 3253)」(p.49)
のコードをPythonで書き直したものとなっています。

入力

N\\L_1\,…\,L_N

入力例



3
8 5 8

解答

# 入力
N = int(input())
L = list(map(int,input().split()))

ans = 0

# 坂が1本になるまで適用
while N > 1:
    # 一番短い板mii1、次に短い板mii2を求める
    mii1 = 0
    mii2 = 1
    if L[mii1] > L[mii2]:
        mii1, mii2 = mii2, mii1
    
    for i in range(2, N):
        if L[i] < L[mii1]:
            mii2 = mii1
            mii = i
        elif L[i] < L[mii2]:
            mii2 = i
    
    # それらを併合
    t = L[mii1] + L[mii2]
    ans += t
    
    if mii1 == N - 1:
        mii1, mii2 = mii2, mii1
    L[mii1] = t
    L[mii2] = L[N - 1]
    N -= 1

print(ans)


O({N}\,log\,{N})で解く別解についても説明しようと思ったのですが、どうやら2-4で説明されているようなのでそこで解説しようと思います。

Pythonで理解する蟻本「2-2 Saruman's Army(POJ 3069)」(p.47)

この記事は「プログラミングコンテストチャレンジブック第2版」(蟻本)の
「2-2 Saruman's Army(POJ 3069)」(p.47)
のコードをPythonで書き直したものとなっています。

入力

N\,R\\X_{1}\,X_{2}\,…\,X_{n}

入力例



6 10
1 7 15 20 30 50


解答

N, R = map(int,input().split())
X = list(map(int,input().split()))

X.sort()

i = 0
ans = 0
while i < N:
    # sはカバーされていない一番左の点の位置
    s = X[i]
    i += 1
    # sから距離Rを超える点まで進む
    while i < N and X[i] <= s + R:
        i += 1
    
    # pは新しく印を付ける点の位置
    p = X[i - 1]
    # pから距離Rを超える点まで進む
    while i < N and X[i] <= p + R:
        i += 1
    ans += 1

print(ans)

Pythonで理解する蟻本「2-2 Best Cow Line(POJ 3617)」(p.45)

この記事は「プログラミングコンテストチャレンジブック第2版」(蟻本)の
「2-2 Best Cow Line(POJ 3617)」(p.45)
のコードをPythonで書き直したものとなっています。

入力

N\\S

入力例



6
ACDBCB


解答

蟻本ではputcharで一文字ずつ出力していますが、Pythonで出力するための標準関数であるprintは出力の際に自動的に改行が挟まれてしまいます。

そのため、下のコードでは文字列Tに文字を追加していき、最後に文字列Tを出力しています。

# 入力
N = int(input())
S = input()

# S[a], S[a+1], ..., S[b]が残っている文字列
a = 0
b = N - 1

T = ''    # 文字列T

while a <= b:
    # 左からみた場合と右から見た場合を比較
    left = False
    for i in range(b - a + 1):    # 0 <= i <= b - a
        if S[a + i] < S[b - i]:
            left = True
            break
        elif S[a + i] > S[b - i]:
            left = False
            break
    # 文字列Tに文字を追加する
    if left:
        T += S[a]
        a += 1
    else:
        T += S[b]
        b -= 1

print(T)

Pythonで理解する蟻本「2-2 区間スケジューリング問題」(p.43)

この記事は「プログラミングコンテストチャレンジブック第2版」(蟻本)の
「2-2 区間スケジューリング問題」(p.43)
のコードをPythonで書き直したものとなっています。

入力

n\\s_{1}\,s_{2}\,…\,s_{n}\\t_{1}\,t_{2}\,…\,t_{n}

入力例



5
1 2 4 6 8
3 5 7 9 10


解答

MAX_N = 100000

# 入力
N = int(input())
S = list(map(int,input().split()))
T = list(map(int,input().split()))

# 仕事をソートするための配列itv
itv = []

# 終了時間の早い順にしたいため、Tを配列の一つ目に、Sを二つ目に入れる
for i in range(N):
    itv.append([T[i], S[i]])
itv.sort()

# tは最後に選んだ仕事の終了時間
ans = 0
t = 0
for i in range(N):
    if t < itv[i][1]:
        ans += 1
        t = itv[i][0]
print(ans)

Pythonで理解する蟻本「2-2 硬貨の問題」(p.42)

この記事は「プログラミングコンテストチャレンジブック第2版」(蟻本)の
「2-2 硬貨の問題」(p.42)
のコードをPythonで書き直したものとなっています。

入力

C_{1}\,C_{5}\,C_{10}\,C_{50}\,C_{100}\,C_{500}\\A

入力例



3 2 1 3 0 2
620


解答

# コインの金額
V = [1, 5, 10, 50, 100, 500]

# 入力
C = list(map(int,input().split()))    # C[0] = C_1, C[1] = C_5, ...
A = int(input())

ans = 0

for i in range(5, -1, -1):
    t = min(A // V[i], C[i])    # コインiを使う枚数
    A -= t * V[i]
    ans += t

print(ans)

Pythonで理解する蟻本「2-1 特殊な状態の列挙」(p.39)

この記事は「プログラミングコンテストチャレンジブック第2版」(蟻本)の
「2-1 特殊な状態の列挙」(p.39)
のコードをPythonで書き直したものとなっています。

PythonC++の違い

C++ではnext_permutationという関数を使って順列を生成していますが、Pythonではitertoolsモジュールのpermutations関数を使うことで順列を要素としたイテレータを生成できます。

Pythonで組み合わせを扱いたいときは、とりあえずitertoolsを使うことを考えてみるとよいでしょう。


コード

MAX_N = 10 ** 8
used = [0] * MAX_N
perm = [0] * MAX_N

# {0,1,2,3,4, ... ,n - 1}の並び替えn!通りを生成する

def permutation1(pos, n):
    if pos == n:
        '''
        permに対する操作
        '''
        return

    # permのpos番目を0~n-1のどれにするかのループ
    for i in range(n):
        if not used[i]:
            perm[pos] = i
            # iを使ったのでフラグをTrueにしておく
            used[i] = True
            permutation1(pos + 1, n)
            # 戻ってきたらフラグを戻しておく
            used[i] = False
    return

from itertools import permutations

def permutation2(n):
    perm2 = list(range(n))
    for tmp_perm in permutations(perm2, n):
        '''
        tmp_permに対する操作
        '''
    return


具体的な実行例も下に載せておきます。


実行例

サンプルコード

MAX_N = 10 ** 8
used = [0] * MAX_N
perm = [0] * MAX_N

# {0,1,2,3,4, ... ,n - 1}の並び替えn!通りを生成する

def permutation1(pos, n):
    if pos == n:
        print(perm[:n])
        return

    # permのpos番目を0~n-1のどれにするかのループ
    for i in range(n):
        if not used[i]:
            perm[pos] = i
            # iを使ったのでフラグをTrueにしておく
            used[i] = True
            permutation1(pos + 1, n)
            # 戻ってきたらフラグを戻しておく
            used[i] = False
    return

from itertools import permutations

def permutation2(n):
    perm2 = list(range(n))
    for tmp_perm in permutations(perm2, n):
        print(tmp_perm)
    return

permutation1(0, 3)
print()
permutation2(3)

実行結果

[0, 1, 2]
[0, 2, 1]
[1, 0, 2]
[1, 2, 0]
[2, 0, 1]
[2, 1, 0]

(0, 1, 2)
(0, 2, 1)
(1, 0, 2)
(1, 2, 0)
(2, 0, 1)
(2, 1, 0)

Pythonで理解する蟻本「2-1 Lake Counting(POJ No.2386)」(p.35)

この記事は「プログラミングコンテストチャレンジブック第2版」(蟻本)の
「2-1 Lake Counting(POJ No.2386)」(p.35)
のコードをPythonで書き直したものとなっています。

入力

N\,M\\(大きさ{N}\times{M}の庭)

入力例



10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.


解答

# 入力
N, M = map(int,input().split())
field = [list(input()) for _ in range(N)] # 庭

# 現在位置(x,y)
def dfs(x, y):
    # 今いるところを.に置き換える
    field[x][y] = '.'
    
    # 移動する8方向をループ
    for dx in range(-1, 2):
        for dy in range(-1, 2):
            # x方向にdx y方向にdy 移動した場所を(nx, ny)とする
            nx = x + dx
            ny = y + dy
            
            # nxとnyが庭の範囲内かどうかとfield[nx][ny]が水溜りかどうかを判定
            if 0 <= nx < N and 0 <= ny < M and field[nx][ny] == 'W':
                dfs(nx, ny)
    return

res = 0
for i in range(N):
    for j in range(M):
        if field[i][j] == 'W':
            # Wが残っているならそこからdfsをはじめる
            dfs(i, j)
            res += 1
print(res)