• home
  • about
  • 全ての投稿
  • ソフトウェア・ハードウェアの設定のまとめ
  • 分析関連のまとめ
  • ヘルスケア関連のまとめ
  • 生涯学習関連のまとめ

greedy

date: 2020-11-15 excerpt: greedyアルゴリズムについて

tag: greedyalgorithm


greedyアルゴリズムについて

概要

  • 現在が良ければ未来も良いはず、という一部のスケジューリング問題に基づく
    • 短期的な最大となる行動を積み重ねると、最適値につくと期待できる場合に使えるアルゴリズム
    • 制約; 途中から使用可能になったタスクは消化しない限り実行不能になることはない、という条件が必要
  • 証明
    • あるときに最良の選択をしなかった場合、後日、最良の選択をした場合と交換可能である
    • あるときに最良の選択をしなかった場合、後日、最良の選択を選べないまま終える可能性がある(= より良い選択があるのに選べないで終わる)

覚えておくと便利

  • /different_strokes/
    • greedyで取るための指標を差分で求めて行う方法

例; 典型的な例(現在選べる最大の効用を選択)

問題

  • 第二回 アルゴリズム実技検定; F - タスクの消化

解答

N = int(input())

X = []
for i in range(N):
    X.append([])

for i in range(N):
    a, b = list(map(int, input().split()))
    X[a - 1].append(b)

cnt = [0] * 101
ans = 0

for d in range(N):
    # d日までのポイントの大きさをカウント
    for b in X[d]:
        cnt[b] += 1
    # 最大のものを一つ選びデクリメント
    for b in range(100, 0, -1):
        if cnt[b] > 0:
            ans += b
            cnt[b] -= 1
            break
    print(ans)

例; 締切と所要時間があるときすべてのタスクを処理できるかどうか

問題

  • AtCoder Regular Contest 092; C - 2D Plane 2N Points

考え方

  • 締切が近いものからやっていく
    • 締切が近いものからやなかったときは破綻する条件がある = 締切が近いものから埋めれば良い

解答

N = int(input())
AB=[list(map(int, input().split())) for _ in range(N)]
AB.sort(key=lambda x:x[1])

acc = 0
for a, b in AB:
    acc += a
    if b < acc:
        print("No")
        exit()
print("Yes")

類題

  • キーエンス プログラミング コンテスト 2020; B - Robot Arms
    • 提出

例; 複数の大きさの異なる箱に、価値と大きさが異なる荷物を詰めるとき、どの入れ方ならば最適か

問題
AtCoder Beginner Contest 195; D - Shipping Center

考え方

  • 2つ指標がある
    • 価値が大きいものからその価値の荷物が入る最小の箱を探す
    • 小さな箱から見ていき、その箱に入る最大の価値の荷物を入れる

解答

  • colab

例; 積めるものから荷物を詰めていく

問題

  • AtCoder Regular Contest 006; C - 積み重ね

解説

  • 積める状態のものから何も気にせず積んでいく

解答

N = int(input())

G = []
for n in range(N):
    w = int(input())
    for g in G:
        if g[-1] >= w:
            g.append(w)
            break
    else:
        G.append( [w] )
print(len(G))

例; 後ろから最適値を埋めていく

問題

  • AtCoder Beginner Contest 137; D - Summer Vacation

解説

  • 後ろから最適値を探すのが最善手
  • 最適値の検索がheapを使わないと難しい点がポイント

解答

import heapq
import collections
N, M = map(int, input().split())

dg = collections.defaultdict(list)
for n in range(N):
    day, gain = map(int, input().split())
    dg[day].append(gain)

que = []
ans = 0
for m in range(1, M + 1):  # 最後の日から見ていく
    for gain in dg[m]:
        heapq.heappush(que, -gain)
    if que:
        ans += -heapq.heappop(que)
print(ans)

例; 橋を最低何個壊せばすべての条件を満たすか

問題

  • AtCoder Beginner Contest 103; D - Islands War

解説

  • 最も後ろの橋を壊したら最大限の条件を満たす
  • 後ろからソートして、一番うしろの橋を壊していく

解答

N,M=map(int,input().split())

AB = []
for _ in range(M):
    AB.append(list(map(int,input().split())))
AB.sort(key=lambda x:x[1])

tgt = -1
cnt = 0
for a, b in AB:
    if not(a < tgt <= b):
        tgt = b
        cnt += 1

print(cnt)

例; 特殊な指標を定義する

  • different-strokes

例; 二次元の差分でペアを作るとき最大の数になるようにペアを作る

問題

  • AtCoder Regular Contest 092; C - 2D Plane 2N Points

解説

  • (証明)ソートした上で、あるペアを(r_a, b_b)と(r_b, b_a)というペアがあったとき、r_a < r_bならば交換可能であり、(r_a, b_a)と(r_b, b_b)とすることができるが、逆はできないことがある
    • 青い点より x, y 座標が小さく,まだ仲良しペアになっていない赤い点を探す
    • なかったらなにもしない
    • あったら,その中で最もy座標が大きいものを探し,仲良しペアにする

解答

N = int(input())

red = [list(map(int,input().split())) for _ in range(N)]
blue = [list(map(int,input().split())) for _ in range(N)]
"""考え方; (x,y)でソートしたblueで、redは条件を満たす中で最もyが大きいものを選ぶ"""

"""blueをソートする x, yの順"""
blue.sort()

"""redをソートする -y,xの順"""
red.sort(key=lambda x: (-x[1], x[0]))


matchs = set()
for bx, by in blue:
    for ax, ay in red:
        if ax < bx and ay < by:
            if (ax, ay) in matchs:
                continue
            else:
                matchs.add( (ax, ay) )
                break
print(len(matchs))

例; カッコの対応が成立するかの判定

問題

  • AtCoder Beginner Contest 167; F - Bracket Sequencing

解説

  • 2つの組に分ける
    • lが多い組 -> rの小さい順にソート
    • rが多い組 -> lが大きい順にソート

解答

N = int(input())
L = []
R = []
for _ in range(N):
    l, r = 0, 0
    s = input()
    for i in range(len(s)):
        if s[i] == "(":
            l += 1
        else:
            if l:
                l -= 1
            else:
                r += 1
    if l >= r:
        L.append([l, r])
    else:
        R.append([l, r])
 
L.sort(key=lambda x: x[1]) # Rが小さい順で並べる
R.sort(key=lambda x: -x[0]) # Lが大きい順で並び替える
arr = L + R
cnt = 0
for l, r in arr:
    if cnt < r:
        print("No")
        exit()
    cnt += l - r # 残りl数
if cnt == 0:
    print("Yes")
else:
    print("No")

例; なるべく近い方を選ぶgreedy

問題

  • AtCoder Beginner Contest 083; C - Multiple Gift

解答

  • atcoder


greedyalgorithm Share Tweet