[アルゴリズム]2週目グリディ


Greedy Greedy


貪欲なアルゴリズム

  • 最適年に近い値を得るための
  • オプションを選択し、一瞬ごとに最適なオプションを選択します.
    ローカル最適解の検索2479182

    条件

  • 貪欲選択属性:選択前は選択に影響しない(選択を再考しない)
  • 最適局所構造:問題の最適解決方法局所問題の最適解決方法からなる構造
    ジルコニア

    ダイナミックプログラミングとの違い


    アルゴリズムの方法と問題の性質が異なる
    DP:サブ問題の最適な解決策を見つけ、これらの結果を組み合わせた情報に基づいてグローバル最適な解決策を選択する.
    Greedy:各ステップは、ローカルの最適解を探す方法で処理され、問題の数を減らします.
    ААААА

    アルゴリズムタイプ



    コイン問題


    価格をお支払いの際、コインの数を最小限に抑えるため
    ¥¥¥¥最大のコインからお支払い
    def min_coin_count(value,coins):
    	total=0
        details=list()
    	coins.sort(reverse=True)
        for coin in coins:
        	coin_num = value//coin
            total+=coin_num
            value-=coin_num*coin
            details.append([coin,coin_num])
        return total,details

    一部リュック問題分割式Kapsack Prob


    重量をkに制限したリュックサックに入れ、最大の価値を得る
    すべてのものは重量と価値で表現されています.
    次のように分類します.
    (不可分なリュックの問題は0/1 Knapsack Problem(ダイナミックプログラミング)
    datas=[(10,10), ...]
    def get_max_value(datas,capacity):
    	datas = sorted(datas,key=lambdㄱa x:x[1]/x[0],reverse=True)
        	# 어떤 기준으로 정렬할 것인지 선택하는 것이 최적 선택의 시작
        total=0
        details=list()
        
        for data in datas:
        	if capacity - data[0]>=0: # 통째로 넣기
            	capacity-=data[0]
                total+=data[1]
                details.append(data[0],data[1],1])
            else: # 쪼개서 넣기
            	fraction = capacity/data[0]
                tota+=data[1]*fraction
                details.append([data[0],data[1],fraction])
                break # 더 이상 계산 안해도 됨
            return total,details
            
    この時点で最高を探しています.その瞬間に最も価値のあるものを選ぶ.しかし、その瞬間のベストタイミングなので、完全にベストとは言えません.

    Kruskal Algorithm


    ネットワーク内のすべての頂点、すなわち幹線に重み付けされたグラフィックを最小コストで解く
    MST(最小費用増加木)が1) 최소 비용의 간선으로 구성됨2) 사이클을 포함하지 않음の条件に従って、各段階でサイクルを実現しない最小費用幹線を選択する.

    短距離順に幹線を含める

  • すべてのノードは、最低コストで
  • に接続する必要があります.
  • すべての幹線情報を重み付け基準とし、昇順に並べ替えた後、コストの低い幹線から順にグラフに入れればよい.
  • アクション

  • 幹線選択に基づいて、
  • を含む
  • までのステップに関係なく、無条件に最小幹線
  • を選択する.
    に注意
  • で選択した一連の幹線に次の幹線を追加したときにサイクルを作成するかどうかを確認します.
    ¥union-findアルゴリズムを使用:追加する幹線が両端の頂点の同じ集合に属しているかどうかを確認します.
    実際,これはソートアルゴリズムとunion−findアルゴリズムの総和と見なすことができる.

    コード#コード#


    #1
    graph=[(1,2,13),....] #(정점1, 정점2, 가중치)
    graph.sort(key=lambda x:x[2])
    
    mst=[]
    n=len(graph)
    p=[0] # 상호 배타적 집합 <<??
    
    for i in range(1,n+1):
    	p.append(i) # 각 정점 자신이 집합의 대표
    def find(u):
        if u!=p[u]:
        	p[u]=find(p[u]) # 경로 압축
        return p[u]
    def union(u,v):
        root1=find(u)
        root2=find(v)
        p[root2]=root1 # 임의로 root2가 root1의 부모
    
    tree_edges=0 # 간선 개수
    mst_cost=0 # 가중치 합
    while True:
        if tree_edges==n-1:break
        u,v,wt = graph.pop(0)
        if find(u)!=find(v): # u와 v가 서로 다른 집합에 속해 있으면
        union(u,v)
        mst.append((u,v))
        mst_cost+=wt
        tree_edges+=1
    #2
    # 특정 원소가 속한 집합을 찾기
    def find(parent, x):
        if parent[x] == x:
            return x
        parent[x] = find(parent, parent[x])
        return parent[x]
    
    
    # 두 원소가 속한 집합을 합치기 (간선 연결한다고 생각!)
    def union(parent, a, b):
        rootA = find(parent, a)
        rootB = find(parent, b)
    
        if rootA < rootB:
            parent[rootB] = rootA
        else:
            parent[rootA] = rootB
    
    
    import sys
    
    input = sys.stdin.readline
    # 노드의 개수와 간선(union 연산)의 개수 입력받기
    v, e = map(int, input().split())
    parent = [0] * (v + 1)
    
    edges = []
    result = 0
    
    # 부모 테이블상에서, 부모를 자기 자신으로 초기화
    for i in range(1, v + 1):
        parent[i] = i
    
    # 모든 간선에 대한 정보를 입력받기
    for _ in range(e):
        a, b, cost = map(int, input().split())
        # 비용순으로 오름차순 정렬하기 위해 튜플의 첫 번째 원소를 비용으로 설정
        edges.append((cost, a, b))
    
    edges.sort()
    
    for edge in edges:
        cost, a, b = edge
        # 사이클이 발생하지 않는 경우에만 집합에 포함(=연결한다.)
        if find(parent, a) != find(parent, b):
            union(parent, a, b)
            result += cost
    
    print(result)
    
    # sample input
    # 7 9
    # 1 2 29
    # 1 6 75
    # 2 3 35
    # 2 6 34
    # 3 4 7
    # 4 6 23
    # 4 7 13
    # 5 6 53
    # 6 7 25

    時間の複雑さ

  • union-findアルゴリズムにより,Kruskalアルゴリズムの時間的複雑度は幹線ソート時間程度で
  • となる.
  • のような高速ソートのような効率的なアルゴリズムを用いてソートする場合、O(eloge)
  • [比較][Prim](https://gmlwjd9405.github.io/2018/08/30/algorithm-prim-mst.html)アルゴリズムの時間複雑度はO(n^2)である.
  • 幹線の少ない疎な図形であれば、Kruskalは
  • に適している.
  • 本以上の幹線を有する密集グラフであれば、Primは適切である.
  • 貪欲なアルゴリズムの限界

  • 近似推定
  • が必ずしも最良とは限らない.最適な年に近い値を取得する方法の1つ
    最適であることを確認する必要があります
    以下の条件を満たす場合に最適

  • 最適な局所構造がある場合
    問題を一部の問題に分けて解決し、問題のある答えを探す.

  • 欲張りな選択属性を持つ場合
    答えのすべての面を考えず、貪欲な選択だけをすれば、最適な答えを見つけることができます.
  • Tip
    多くのグリディアルゴリズムは、問題を解決するための最低限の考えを考え、それが正当かどうかを研究してこそ、答えを出すことができる.符号化テストの問題が発生した場合、問題のタイプをすぐに特定することが困難である場合、Gredyアルゴリズムが疑われる.
    [注意]
  • Kruskal
  • https://gmlwjd9405.github.io/2018/08/29/algorithm-kruskal-mst.html
  • https://blog.naver.com/ndb796/221230994142
    https://cieske.tistory.com/54