链接: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;
}