AcWing 1077. 皇宮看守(詳解)

題目描述

        太平王世子事件後,陸小鳳成了皇上特聘的御前一品侍衛。皇宮各個宮殿的分佈,呈一棵樹的形狀,宮殿可視爲樹中結點,兩個宮殿之間如果存在道路直接相連,則該道路視爲樹中的一條邊。已知,在一個宮殿鎮守的守衛不僅能夠觀察到本宮殿的狀況,還能觀察到與該宮殿直接存在道路相連的其他宮殿的狀況。大內保衛森嚴,三步一崗,五步一哨,每個宮殿都要有人全天候看守,在不同的宮殿安排看守所需的費用不同。可是陸小鳳手上的經費不足,無論如何也沒法在每個宮殿都安置留守侍衛。幫助陸小鳳佈置侍衛,在看守全部宮殿的前提下,使得花費的經費最少。

輸入格式

輸入中數據描述一棵樹,描述如下:

第一行 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問題,顯然每個節點有放置守衛和不放置守衛兩種,但是從計算的過程看,不放置守衛的狀態由兩種,一種是有其父節點上的守衛看護,一種是由其子節點的守衛看護,因此可將每個節點的看護情況分爲三種:

  1. 該節點由父節點處放置的守衛看護
  2. 該節點由子節點處放置的守護看護
  3. 該節點由在該節點放置的守衛看護

下面考慮狀態轉移的過程,建立數組f[i][3],其中

  1. f[i][0]表示第i個節點由父節點處放置的守衛看護下的最小代價
  2. f[i][1]表示第i個節點由子節點處放置的守衛看護下的最小代價
  3. f[i][2]表示第i個節點由在該節點放置的守衛看護下的最小代價

那麼可以寫出轉移關係:

  1. f[i][0] += min(f[j][1], f[j][2]);
  2. f[i][1] = min(f[i][1], sum - min(f[j][1], f[j][2]) + f[j][2]);
  3. 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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章