藍橋杯 演算法訓練 結點選擇

2021-10-24 13:27:54 字數 4179 閱讀 7862

題目

問題描述

有一棵 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 edge

;其中edge[i].to表示第i條邊的終點,

edge[i].next表示與第i條邊同起點的上一條邊的存位置,

edge[i].w為邊權值.

另外還有乙個陣列head,在加邊函式中作用較大。

head陣列一般初始化為-1,對於加邊的add函式是這樣的:

cnt = 0;//作為全域性變數

void add(int u,int v,int w)

會發現,head[i]儲存的是以i為起點的所有邊中編號最大的那個,在遍歷中可以借這個作為迴圈的初始值,進行逆序迴圈(以i為起點的邊,從最後生成的那條開始,逐條向前遍歷,直至遍歷完所有以i為頂點的邊)

在遍歷以u節點為起始位置的所有邊的時候是這樣的:

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 ...