huyichen世子事件後,xuzhenyi成了皇上特聘的御前一品侍衛。 皇宮以午門爲起點,直到後宮嬪妃們的寢宮,呈一棵樹的形狀;某些宮殿間能夠互相望見。大內保衛森嚴,三步一崗,五步一哨,每一個宮殿都要有人全天候看守,在不一樣的宮殿安排看守所需的費用不一樣。 但是xuzhenyi手上的經費不足,不管如何也無法在每一個宮殿都安置留守侍衛。 幫助xuzhenyi佈置侍衛,在看守所有宮殿的前提下,使得花費的經費最少。atom
輸入文件中數據表示一棵樹,描述以下:spa
第1行 nn,表示樹中結點的數目。code
第2行至第n+1n+1行,每行描述每一個宮殿結點信息,依次爲:該宮殿結點標號i(0<i≤n)i(0<i≤n),在該宮殿安置侍衛所需的經費kk,該邊的兒子數mm,接下來mm個數,分別是這個節點的mm個兒子的標號r1,r2,...,rmr1,r2,...,rm。xml
對於一個n(0<n≤1500)n(0<n≤1500)個結點的樹,結點標號在1到nn之間,且標號不重複。blog
輸出文件僅包含一個數,爲所求的最少的經費。事件
因爲宮殿節點圖是樹的形狀,因此很明顯這是樹形DP。it
首先二維狀態,dp[i][j],j∈{1,2,3}io
dp[i][1]表示這個點被本身守衛。class
dp[i][2]表示這個點被父親守衛。搜索
dp[i][3]表示這個點被兒子守衛。
根據屬性DP慣用套路,首先大法師(DFS)搜索到葉節點,而後向上更新。
若是這個點被本身守衛,那麼他的兒子可能有三種狀態,既多是本身守衛,又可能被父親守衛,還有可能被它的兒子守衛。
用s來表明x的兒子,因此:
dp[x][1] += min(dp[s][2],min(dp[s][3],dp[s][1]));
若是這個點被父親守衛,那麼他的兒子只可能被本身守衛,或者被它的兒子守衛。
dp[x][2] += min(dp[s][1],dp[s][3]);
那麼最困難的是這個點被本身的兒子守衛,那麼他的全部兒子一樣是兩種狀態,被本身守衛或者被它的兒子守衛。且必定存在一個兒子被本身守衛。
若是更新了一圈後,發現全部的兒子本身守衛的代價都要大於它們的兒子守衛它們的價值(即x的兒子s守衛的價值大於s的兒子守衛的價值),咱們須要加上一個s本身守衛和s的兒子守衛的差量,而且保證這個差量最小。
注意把全部點本身守衛本身的狀況先賦上值。
代碼:
#include<cstdio> #include<algorithm>
#define N 1555
using namespace std; int money[N]; int son[N][N]; int dp[N][4]; void dfs(int x) { if(!son[x][0]) { dp[x][1] = money[x]; dp[x][3] = money[x]; dp[x][2] = 0; return ; }else { for(int i = 1;i<=son[x][0];i++) { dfs(son[x][i]); } for(int i = 1;i<=son[x][0];i++) { int s = son[x][i]; dp[x][1] += min(dp[s][2],min(dp[s][3],dp[s][1]));//本身
dp[x][2] += min(dp[s][1],dp[s][3]);//父親
} bool flag = 0; int bu = 2147438647; for(int i = 1;i<=son[x][0];i++) { int s = son[x][i]; dp[x][3] += min(dp[s][1],dp[s][3]); if(dp[s][3]>=dp[s][1]) { flag = 1; } bu = min(bu,dp[s][1]-dp[s][3]); } if(flag==0) { dp[x][3]+=bu; } } } int main() { int n; scanf("%d",&n); int root; for(int i = 1;i<=n;i++) { int num; scanf("%d",&num); if(i==1) { root = num; } scanf("%d",&money[num]); dp[num][1] = money[num]; scanf("%d",&son[num][0]); for(int j = 1;j<=son[num][0];j++) { scanf("%d",&son[num][j]); } } dfs(root); printf("%d",min(dp[root][1],dp[root][3])); }