題目描述
太平王世子事件後,陸小鳳成了皇上特聘的御前一品侍衛。皇宮各個宮殿的分佈,呈一棵樹的形狀,宮殿可視爲樹中結點,兩個宮殿之間如果存在道路直接相連,則該道路視爲樹中的一條邊。已知,在一個宮殿鎮守的守衛不僅能夠觀察到本宮殿的狀況,還能觀察到與該宮殿直接存在道路相連的其他宮殿的狀況。大內保衛森嚴,三步一崗,五步一哨,每個宮殿都要有人全天候看守,在不同的宮殿安排看守所需的費用不同。可是陸小鳳手上的經費不足,無論如何也沒法在每個宮殿都安置留守侍衛。幫助陸小鳳佈置侍衛,在看守全部宮殿的前提下,使得花費的經費最少。
輸入格式
輸入中數據描述一棵樹,描述如下:
第一行 n,表示樹中結點的數目。
第二行至第 n+1 行,每行描述每個宮殿結點信息,依次爲:該宮殿結點標號 i,在該宮殿安置侍衛所需的經費 k,該結點的子結點數 m,接下來 m 個數,分別是這個結點的 m 個子結點的標號 r1,r2,…,rm。
對於一個 n 個結點的樹,結點標號在 1 到 n 之間,且標號不重複。
輸出格式
輸出一個整數,表示最少的經費。
數據範圍
1≤n≤1500
輸入樣例
6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0
輸出樣例
25
樣例解釋
在2、3、4結點安排護衛,可以觀察到全部宮殿,所需經費最少,爲 16 + 5 + 4 = 25。
解題思路
首先這道題的題意是:給定一棵樹,要在一些節點上放置守衛,每個守衛可以看護當前節點以及與此節點連通的節點,在不同節點放置守衛的代價不同,如何選取節點使代價最小,這是個典型的樹形DP問題,顯然每個節點有放置守衛和不放置守衛兩種,但是從計算的過程看,不放置守衛的狀態由兩種,一種是有其父節點上的守衛看護,一種是由其子節點的守衛看護,因此可將每個節點的看護情況分爲三種:
- 該節點由父節點處放置的守衛看護
- 該節點由子節點處放置的守護看護
- 該節點由在該節點放置的守衛看護
下面考慮狀態轉移的過程,建立數組f[i][3],其中
- f[i][0]表示第i個節點由父節點處放置的守衛看護下的最小代價
- f[i][1]表示第i個節點由子節點處放置的守衛看護下的最小代價
- f[i][2]表示第i個節點由在該節點放置的守衛看護下的最小代價
那麼可以寫出轉移關係:
- f[i][0] += min(f[j][1], f[j][2]);
- f[i][1] = min(f[i][1], sum - min(f[j][1], f[j][2]) + f[j][2]);
- f[i][2] += min(min(f[j][0], f[j][1]), f[j][2]);
其中j爲i的子節點,sum爲所有子節點j的min(f[j][1], f[j][2])的和,1和3的意義很明顯,2的意義代表,如果第i個節點由子節點守衛,那麼所有子節點都不能由父節點守衛,並且每個子節點都得到了守衛,且至少有一個子節點處放置了守衛
至此,算法思路就很清晰了,下面就用常規求解過程來寫代碼
代碼
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1510;
int n;
int h[N], e[N], ne[N], idx,w[N];
int f[N][3];
bool st[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void dfs(int u)
{
f[u][2] = w[u];
int sum = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
dfs(j);
f[u][0] += min(f[j][1], f[j][2]);
f[u][2] += min(min(f[j][0], f[j][1]), f[j][2]);
sum += min(f[j][1], f[j][2]);
}
f[u][1] = 1e9;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
f[u][1] = min(f[u][1], sum - min(f[j][1], f[j][2]) + f[j][2]);
}
}
int main()
{
cin >> n;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ )
{
int id, cost, cnt;
cin >> id >> cost >> cnt;
w[id] = cost;
while (cnt -- )
{
int ver;
cin >> ver;
add(id, ver);
st[ver] = true;
}
}
int root = 1;
while (st[root]) root ++ ;
dfs(root);
cout << min(f[root][1], f[root][2]) << endl;
return 0;
}