题目描述
#10157. 「一本通 5.2 例 5」皇宫看守 - 题目 - LibreOJ (loj.ac) 太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。
皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状,某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
输入格式
输入中数据描述一棵树,描述如下:
第一行 ,表示树中结点的数目。
第二行至第 行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号 ,在该宫殿安置侍卫所需的经费 ,该边的儿子数 ,接下来 个数,分别是这个节点的 个儿子的标号 。
对于一个 个结点的树,结点标号在 到 之间,且标号不重复。
输出格式
输出最少的经费
样例
input
6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0
output
25
有六个区域被安排的情况如左图所示。
如右图,灰色点安排了警卫, 号警卫可以观察 , 号警卫可以观察 , 号警卫可以观察 。
总费用:
数据范围与提示
对于 的数据,。
思路
条件和战略游戏很像, 但上一题是求选择的个数, 这里不但要满足条件还要找到最小的摆放位置, 也就是说光知道该点是否被覆盖不行, 得细分为被哪个节点覆盖。
单是两个状态表示当前节点是否放棋子已经无法满足要求, 需要扩增。
状态表示为:
f[i][0]
可以被父节点看到, 且当前点无棋子
f[i][1]
可以被子节点看到, 且当前点无棋子
f[i][2]
当前放棋子
状态转移:
f[i][0] = min(f[j][1], f[j][2])
此时i上无棋子, 子节点可以被其子节点看到或者放棋子
f[i][2] = min(f[j][0], f[j][1], f[j][2])
此时i上有棋子, 子节点随意
f[i][1] = min(f[j][2] + min(f[k][1], f[k][2])) k为除j之外的子节点
需要确定从某一子节点转移过来, 其他的子节点只可能被其子节点看到或者自己放棋子
因为f[i][0]
本身就代表了i子节点的最小值之和
故求f[i][1]
所需要的min(f[k][1], f[k][2])
时, 可以用所有子节点之和减去当前子节点, 这样就能得出来除j之外的子节点之和。
即f[i][1] = min(f[i][1], f[j][2] + f[u][0] - min(f[j][1], f[j][2]))
代码
int j = e[i];
f[u][1] = min(f[u][1], f[j][2] + f[u][0] - min(f[j][1], f[j][2]));
}
}
int main()
{
cin >> n;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i++)
{
int a, m;
scanf("%d", &a);
scanf("%d%d", &v[a], &m);
for (int i = 1; i <= m; i++)
{
int b;
scanf("%d", &b);
add(a, b);
st[b] = true;
}
}
int root = 1;
while (st[root])
root++;
// cout << root << endl;
dfs(root);
printf("%d\n", min(f[root][1], f[root][2]));
return 0;
}