題目
問題描述題目分析有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?
輸入格式
第一行包含乙個整數 n 。
接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。
接下來一共 n-1 行,每行描述樹上的一條邊。
輸出格式
輸出乙個整數,代表選出的點的權值和的最大值。
樣例輸入
51 2 3 4 5
1 21 3
2 42 5
樣例輸出
12樣例說明
選擇3、4、5號點,權值和為 3+4+5 = 12 。
資料規模與約定
對於20%的資料, n <= 20。
對於50%的資料, n <= 1000。
對於100%的資料, n <= 100000。
權值均為不超過1000的正整數。
首先,回顧一下 資料結構中 「節點」的定義:
是資料結構中,用來描述「樹」型結構的名詞。這種結構像一根倒著的樹。每片樹葉都長在乙個結點上,這個結點就叫做這個葉子的父結點,這個葉子叫做父結點的子結點,也叫這棵樹的葉結點,它再沒有子結點了。
經過對問題的分析,發現,需要用到的思想如下:
1、動態規劃的策略進行求解。因為符合每個階
段的決策
,做出的
是一組局
部的決策
結果,而
每個階段
都使問題
規模變小
,且更接
近最優解
\color
每個階段的決
策,做出
的是一組
區域性的決
策結果,
而每個階
段都使問
題規模變
小,且更
接近最優
解2、構造樹----這是乙個樹形結構,但不是二叉樹(題目中的情況表明可以是多叉數),所以可以理
解為乙個
圖,那麼
對它的信
息進行存
儲應該用
圖的儲存
方式
。\color
可以理解為一
個圖,那
麼對它的
資訊進行
儲存應該
用圖的存
儲方式。
對於圖的常用儲存方式為:鄰接矩陣、鄰接表。但是,鄰接矩陣的儲存,是直接分配儲存空間,如果輸入的資料形成的圖(實際上是樹,但是也可以理解為圖)是稀疏有向圖,那麼會造成大量記憶體空間的浪費。所以,採鄰接鏈
表來進行
儲存
\color
鄰接鍊錶來進
行儲存。鑑於題目的資訊輸入,採取的是 邊 的形式輸入,且是按照節點的遞增順序,所以採用鏈式前
向星
\color
鏈式前向
星進行邊集數
組的儲存
\color
邊集陣列的存
儲。3、本題是乙個按照圖的規則儲存的樹,根據題目意思,應該深度遍歷所輸入的樹,對它的節點按照 自下而上(從深度最大的地方向著根節點的方向)進行遍歷。那麼,就需要先進入樹的葉節點,所以 動態規劃的過程應該是:
回溯演算法:先進行遞迴操作,在遞迴到可解的最小值(葉節點)處時,逐步返回原問題的解(返回的是區域性的決策結果)。
所以,在以圖的方式儲存資料的時候,應該將邊儲存為雙向邊(因為有遞迴和回溯兩個方向要進行)。
做一下需要用到的知識的說明
1、動態規劃
動態規劃的每個階段的的決策,作出的是一組區域性的決策結果。每個階段都使問題規模變小,且更接近最優解;直到最後一步,問題的規模變為1,就找到問題的最優解,演算法結束。那麼,
動態規劃 = 貪婪策略 + 遞推(降階)+ 儲存遞推結果
是,全面、分階段地解決問題。是「帶決策的多階段、多方位的地推算法」。
2、構造樹----(鏈式)向前星
建立的邊結構體為:
struct edgehead陣列一般初始化為-1,對於加邊的add函式是這樣的:;其中edge[i].to表示第i條邊的終點,
edge[i].next表示與第i條邊同起點的上一條邊的存位置,
edge[i].w為邊權值.
另外還有乙個陣列head,在加邊函式中作用較大。
cnt = 0;//作為全域性變數在遍歷以u節點為起始位置的所有邊的時候是這樣的:void add(int u,int v,int w)
會發現,head[i]儲存的是以i為起點的所有邊中編號最大的那個,在遍歷中可以借這個作為迴圈的初始值,進行逆序迴圈(以i為起點的邊,從最後生成的那條開始,逐條向前遍歷,直至遍歷完所有以i為頂點的邊)
for(int i=head[u]; i!=-1 ;i=edge[i].next)/*因為陣列head初始化為-1
edge[cnt].next = head[u];//記錄上乙個以cnt作為起點的邊的編號
所以,只有 以 cnt 為起點的第一條邊的上一條邊不存在,即,這個時候i=edge[i].next = -1
3、動態規劃的分析 (狀態轉移 方程) :
對於每個點,有兩個選擇,選和不選。
定義陣列 int dp[max][2] ;
對於第i個結點,
dp[i][0]表示不取該結點,所能達到的最大值
dp[i][0] += max(dp[child][0],dp[child][1])
不取第i個結點,當前結點所能達到的最大值是,等於i的孩子結點取或者不取所能達到的較大值的和;
dp[i][1]表示取該結點,所能達到的最大值
dp[i][1] += dp[child][0]
取第i個結點,當前結點所能達到的最大值是,等於i的每個孩子不取所能達到的最大值的和
4、使用深度搜尋dfs,自頂向下分解,自底向上回溯求解。
5、本 題建
立雙向邊
的注意事
項:
\color
本題建立雙向
邊的注意
事項:
int m=0;
//定義全域性變數 m 的初值為0
// fromnode 是邊的起點, tonode是邊的終點
intaddedge
(int fromnode,
int tonode)
//該樹採用鏈式前向星,並且用雙向邊的方式,儲存邊、查詢邊
程式原始碼
具體思路**中也有注釋:
#include
#include
#define max 100001
int dp[max][2
];//0代表不選擇節點權值;1代表選擇節點權值
int head[max]
;//儲存的是邊的頂點的情況--輔助結構體進行儲存
int m=0;
//計數的全域性變數
struct edge
edge[
2*max]
;//建立結構體,同時定義結構體陣列
intaddedge
(int rootnode,
int goalnode)
intmax
(int a,
int b)
intdfs
(int goalnode,
int rootnode)
return0;
}int
main()
/* 從根節點開始深度優先遍歷
遍歷結束,自底向上進行回溯
(假設根節點的父節點為 0)
**/dfs(1,
0);/*
最後,還需要對根節點進行判斷
因為遞迴的過程,實際上是從 以根節點為起點的情況進行判斷
即,判斷除根節點之外的其他情況
所以,最後還需要對根節點進**況判斷
**/s=max
(dp[1]
[0],dp[1]
[1])
;printf
("%d"
,s);
return0;
}
參考原博文 藍橋杯 演算法訓練 結點選擇
題目 問題描述 有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?輸入格式 第一行包含乙個整數 n 接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。接下來一共 n 1 行,每行描述樹上的一...
藍橋杯 演算法訓練 結點選擇
問題描述 有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?輸入格式 第一行包含乙個整數 n 接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。接下來一共 n 1 行,每行描述樹上的一條邊。...
藍橋杯 結點選擇
問題描述 有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?解題思路 這題模型是樹形動態規劃入門題目,dp i 0 表示該節點不被選擇,dp i 1 表示該結點被選擇。轉移方程為 dp u 1 dp v 0 ...