ツリーの独立セット:TreeDP
17069 ワード
質問する
2213号:ツリーの独立集合
概要
これは、ツリーの最大独立セットを求め、その構成ノードを出力する必要があるという問題です.
この場合、最大独立セットは、独立セットの構成要素がグラフィックに隣接せず、要素の重みの和が最大である必要があります.
ツリーの独立セット
問題を解く
アクセスと時間の複雑さの計算
この問題はどのような方法で処理すべきですか.
まず、すべてのノードを最大独立セット(1)に含めることも、(2)を含まないこともできます.
すなわち,すべてのノードが2つの場合の数を共有する.
最もNaiveの解はO(2 N)O(2^N)O(2 N)の解が考えられる.NNNが大きいので当然タイムアウトです.)
この場合,時間的複雑さを低減する方向を考慮すべきである.
そう考えると、1≦N≦100001≦N≦100001≦N≦10000なので、O(Nlogn)O(Nlogn)O(Nlogn)、O(N)O(N)O(N)の時間的複雑さが一般的に考えられる.
O(Nlogn)O(Nlogn)O(Nlogn)O(Nlogn)には、ソート、二分検索、ソート、累積、データセグメントツリー化などがある.しかし,これはすべての状況を考慮した問題であるため,O(Nlogn)O(Nlogn)O(Nlogn)プールは一時的に保持できる.
では早速O(N)O(N)O(N)O(N)
ルートノードからリーフノードまで、上から下まで演算して問題を解決する必要があります.
せんたくアルゴリズム
この問題のNaive Han Paulをもう一度整理しましょう.
これはすべての状況の数を考慮してその中から最値を選択する必要があるという問題であるため,以前に用いた記録を用いたアルゴリズムを考えることができる.
選択可能なアルゴリズムはMemoization/DPである.また,この問題の解答はツリーの場合のDPと考えられる.
問題の簡略化
では、DPの属性を考えてみましょう.
dp[i][1]dp[i][1]dp[i][1]=iをルートノードとするサブツリーにiを含む場合、ツリー内の独立セットの最大値は
ツリーの場合のDPは、通常、ツリーDPと呼ばれる.
ツリーDPでは、リーフノードの状況を知ることが重要である.
この問題では、リーフノードの場合は次のようになります.
リーフノードの状況:[1],[0]を含まないまたは含まない
リフノルドの場合は図のようです.また、それぞれの場合、dp[leaf][j]dp[leaf][j]dp[leaf][j]はリーフノードの重みに等しい.
次に、リーフノードではなくノードの場合を見てみましょう.
ic=ii c=iic=iノードのサブノード
Dp[i]Dp[i]Dp[i]では、次のようになります.これはすでに上記の内容です.
自分が含まれている場合は、サブノードが含まれているとは限りません.
最大独立セット値は
最大独立セット値
逆トレースDPパス
では、どのようにして重み付けとパスを実現しますか?
既に作成したDP Tableを利用すればよい
経路は1番ノード(木のルートノード)から最大(Dp[i][0],Dp[i][1])max(Dp[i][0],Dp[i][1])max(Dp[i][0],Dp[i][1])を求める.
max(Dp[i][0],Dp[i][1])=Dp[i][0]max(Dp[i][0],Dp[i][1])=Dp[i][0]max(Dp[i][0],Dp[i][1])=Dp[i][0]であれば、このノードを含まないのが最大値となる.
max(Dp[i][0],Dp[i][1])=Dp[i][1]max(Dp[i][0],Dp[i][1])=Dp[i][1]max(Dp[i][0],Dp[i][1])=Dp[i][1]であれば、そのノードを含む場合が最大値となる.
パスを探すときには、以下の状況と作成したDPテーブルを用いて逆方向追跡を行うこともできます.
最大独立セット値は
最大独立セット値
インプリメンテーション
import sys
sys.setrecursionlimit(10 ** 6)
In = lambda: sys.stdin.readline().rstrip()
def init():
nodes = int(In())
vals = list(map(int, In().split())) # 가중치
vals.insert(0, 0) # dummy value
tree = []
check = [0] * (nodes + 1)
dp = [[-1, -1] for i in range(nodes + 1)]
for i in range(nodes - 1):
nodeX, nodeY = list(map(int, In().split()))
tree[nodeX].append(nodeY)
tree[nodeY].append(nodeX)
return tree, dp, vals
def dynamicProgramming(cur, prev):
dp[cur][1] = vals[cur]
dp[cur][0] = 0
for nextnode in tree[cur]: # 자식노드 탐색, # 리프노드의 경우 for문 X
if nextnode != prev:
dynamicProgramming(nextnode, cur)
dp[cur][1] += dp[nextnode][0]
dp[cur][0] += max(dp[nextnode][0], dp[nextnode][1])
def findRoute(cur, prev, isVisited):
if isVisited:
answr.append(cur)
for nextnode in tree[cur]:
if nextnode != prev:
findRoute(nextnode, cur, 0) # 현재 노드를 포함 -> 자식 노드 포함 X
else:
for nextnode in tree[cur]:
if nextnode != prev:
if dp[nextnode][0] > dp[nextnode][1]:
findRoute(nextnode, cur, 0) # 현재 노드 포함 X-> 자식노드 포함 X
else:
findRoute(nextnode, cur, 1) # 현재 노드 포함 X -> 자식노드 포함 O
if __name__ == "__main__":
tree, dp, vals = init()
dynamicProgramming(1, -1)
answr = []
if dp[1][0] > dp[1][1]:
findRoute(1, -1, 0)
else:
findRoute(1, -1, 1)
answr.sort()
print(dp[1][0] if dp[1][0] > dp[1][1] else dp[1][1])
print(*answr)
Reference
この問題について(ツリーの独立セット:TreeDP), 我々は、より多くの情報をここで見つけました https://velog.io/@seilk/백준-트리의-독립집합-TreeDPテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol