問題描述
有一棵 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、建樹。2、動態規劃。
剛開始選擇的儲存結構是二維陣列,既每一行表示樹的一層,每一列表示該層(行)的所有節點;記錄下樹的最大層數,從最後一層開始改變每個節點的狀態,最後從根節點中獲取最優解。
#include#include#include#define m 100010 //陣列最大長度int fu[m],hz[m][m],shu[m][m],pow[m],f[m][2];
//父節點陣列; 孩子陣列hz[i][0]第i個節點的孩子數,hz[i][j](j>0)表示i節點的第j個孩子
//樹二維陣列,shu[i][0]表示第i層節點數,shu[i][j](j>0)表示第i層的第j個節點;
//pow權值陣列,p[i]表示第i個節點的權值
//f[i][1]保留節點i時最大權值,f[i][0]不保留節點i時的最大權值
int main()
//動態規劃
int now,k,a,b;
for(i=maxlev;i>0;i--)
else}}
}int sum=0;
for(i=1;i<=shu[1][0];i++)
printf("%d\n",sum);
return 0;
}
按理說這個演算法是可行的,但是再提交答案時,居然發生執行錯誤,我看了看記憶體使用率非常大,返回題目看了資料規模,節點數n<=100000,也就意味著要用二維陣列儲存樹的話,二維陣列至少定義為shu[100000][100000],占用了非常大的控制項資源。再者,題目給n個頂點,n-1條邊,也就意味著樹沒有孤立點,並且有且僅有乙個根節點,可見每一層的節點很多時候是遠少於100000的,所以應該改用動態儲存結構。
樹的儲存結構
《1》、雙親表示法
假設以一組連續空間儲存樹的節點,同時在每個節點中附設乙個指示器指示其雙親節點在鍊錶中的位置,其形式說明如下:
#define max_tree_size 100typedef struct ptnodeptnode;
typedef structptree;
這種儲存結構利用了每個節點(除根節點以外)只有唯一雙親的性質。parent(t,x)操作可以在常數時間內實現。反覆呼叫parent操作,直到遇見無雙親的節點時,便找到了樹的根,這個就是root(x)的過程。但是,在這種表示法中,求節點的孩子時需要遍歷整個結構。
《2》、孩子表示法
這裡主要給出一種類似於鄰接表的表示法。把每個節點的孩子節點排列起來,看成是乙個線性表,且以單鏈表作為儲存結構,則n個節點有n個孩子鍊錶(葉子節點的孩子鍊錶為空表)。而n個頭指標又組成乙個線性表,為了便於查詢,可採用順序儲存結構。這種儲存結構可形式地說明如下:
#define max_tree_size 100typedef struct ctnode*childptr;
typedef structctbox;
typedef structctree;
與雙親表示法相反,孩子表示法便於那些涉及孩子操作的實現,卻不適合用於parent(t,x)的操作。我們可以把雙親表示法和孩子表示法合起來,既將雙親表示和孩子鍊錶和在一起。
《3》、孩子兄弟表示法
又稱二叉樹表示法,或二叉樹表示法。既以二叉樹表作樹的儲存結構。鍊錶中節點的兩個鏈域分別指向該節點的第乙個孩子節點和下乙個兄弟節點,分別命名為firstchild域和nextsibling域。儲存結構形式說明如下:
#define max_tree_size 100typedef struct csnodecsnode,*cstree;
利用這種結構便於實現各種樹的操作。
符合題目要求的結果
採用了樹儲存結構中的《孩子表示法》,當然有些改進。
#include#include#include#include#define m 100100 //最大長度using namespace std;
//孩子節點結構
typedef struct node
child;
child* head[m];//煉表頭陣列
int f[m][2],pow[m],visit[m];
//pow權值陣列,p[i]表示第i個節點的權值
//f[i][1]保留節點i時最大權值,f[i][0]不保留節點i時的最大權值
//visit[i]==1表示i點被訪問過,visit[i]==0表示節點i未被訪問過
//新增邊(對稱的)
void addadj(int u,int v)
//動態規劃獲取結果
void getresul(int v)
}
f[v][1]+=pow[v];}
int main()
for(i=1;igetresul(1);//從節點1開始進行動態規劃
printf("%d\n",max(f[1][0],f[1][1]));//結果輸出
return 0;
}
結點選擇 (藍橋杯 樹形動態規劃)
問題描述 有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?輸入格式 第一行包含乙個整數 n 接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。接下來一共 n 1 行,每行描述樹上的一條邊。...
結點選擇(樹形動態規劃)
現在對於樹形動態規劃理解的還不是很透徹,只能先做幾個題目練習一下 本題很重要的兩個方面乙個是建樹,這個需要邊和點的相互配合,乙個點兩條邊的表示都要標記。第二個很重要的方面是動態規劃的變化,本題首先想到的一成一成的樹,要不是子集的結合,要不就是自己本身,上乙個點要不是自己本身,要不是自己和子節點的子節...
動態規劃 樹形動態規劃 結點選擇
題目 有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?第一行包含乙個整數 n 接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。接下來一共 n 1 行,每行描述樹上的一條邊。輸出乙個整數,...