链接:http://acm.ocrosoft.com/problem.php?cid=1695&pid=4

题目描述

    太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。

    皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;有边直接相连的宫殿可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。 

    可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。

    编程任务:帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。

输入

    输入文件中数据表示一棵树,描述如下: 

    第1行 n,表示树中结点的数目。 

    第2行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i(0<I<=N),在该宫殿安置侍卫所需的经费K,该点的儿子数M,接下来M个数,分别是这个节点的M个儿子的标号R1,R2,...,RM。

    对于一个n(0 < n<=1500)个结点的树,结点标号在1到n之间,且标号不重复。 

输出

输出文件仅包含一个数,为所求的最少的经费。 

样例输入

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

思路:

能被看到的意思是有边相连hh,给定了树形。dp[][i]表示以i为跟的子树全覆盖所需的最小花费,0表示子树的根被儿子看,1表示子树的根被自己看,2表示子树的根被父亲看。

则转移为dp[0][i]=∑min{dp[0][x],dp[1][x]}+dp[1][y],x为i的儿子,x!=y,y为选定的儿子

dp[1][i]=(求和)min{dp[2][x],dp[1][x]}+a[i],x为i的儿子,a[i]为i的权值

dp[2][i]=(求和)min{dp[0][x],dp[1][x]},x为i的儿子

最后答案就是min{dp[rt][0],dp[rt][1]}

代码:

#include<bits/stdc++.h>

using namespace std;

#define ll long long

#define maxn 1505

#define inf 0x3f3f3f3f

int n, v[maxn], son_num[maxn],son[maxn][maxn],vis[maxn];

ll dp[maxn][3], tmp[maxn];

void tree(int x)

{

    if (dp[x][0])return;

    for (int i = 1; i <= son_num[x]; i++)

    {

        int t = son[x][i];

        tree(t);

        dp[x][0] += min(dp[t][0], min(dp[t][1], dp[t][2]));//如果再x位置上放了首位,那取子节点的最小值即可

        dp[x][2] += dp[t][1]; //如果x位置是被父节点保护的,且x不放首位,为了x的子节点被保护,那x只能有x的子节点的子节点保护

    }

    dp[x][0] += v[x];

    memset(tmp, 0, sizeof(tmp));

    ll tt = 0;

    for (int i = 1; i <= son_num[x]; i++)

    {

        int t = son[x][i];

        tmp[i] = min(dp[t][0], dp[t][1]);

        tt += tmp[i];

    }

    dp[x][1] = inf;//赋初值

    for (int i = 1; i <= son_num[x]; i++)

    {

        int t = son[x][i];

        if (tt - tmp[i] + dp[t][0] < dp[x][1])

            dp[x][1] = tt - tmp[i] + dp[t][0];

    }

}

int main()

{

    int root;

    cin >> n;

    for (int i = 1; i <= n; i++)

    {

        int x;

        cin >> x;

        cin >> v[x] >> son_num[x];

        for (int j = 1; j <= son_num[x]; j++)

        {

            cin >> son[x][j];

            vis[son[x][j]] = 1;

        }

    }

    for (int i = 1; i <= n; i++)

    {

        if (!vis[i])//找根节点

        {

            root = i;

            break;

        }  

    }

    tree(root);//建树

    cout << min(dp[root][0],dp[root][1]) << endl;

      

}