信息学奥赛一本通 1579:【例 5】皇宫看守 | 洛谷 P2458 [SDOI2006] 保安站岗
【题目链接】
ybt 1579:【例 5】皇宫看守
洛谷 P2458 [SDOI2006] 保安站岗
【题目考点】
1. 树形动规
2. 最小支配集
对于图G = (V,E),从V中取尽量少的点组成一个集合,使得V中剩余的点都与取出来的点有边相连。
【解题思路】
以ybt 1579为题目背景:
所有结点构成树形结构。每个宫殿是一个结点,选择一些宫殿留守侍卫,就是在所有结点中选择一些结点。在一个宫殿留侍卫需要的费用,就是该结点的权值。
一个宫殿的侍卫除了可以看守本宫殿,还可以看守与该宫殿有路相连的其他宫殿,也就是可以看守当前结点的邻接点。
一个结点被选择,或是该结点的邻接点被选择,称为该结点被支配。
求在所有结点都被支配的前提下,选择结点的所有方案中,点权加和最小的方案的点权加和。
简单来说,本题求给定树的最小支配集。
状态定义:
-
阶段:以结点u为根的子树
一个结点被支配,可以分为三种情况:
(1)不选择u,且u的双亲被选择(依靠双亲)
(2)不选择u,且u的孩子被选择(依靠孩子)
(3)u被选择(依靠自己)
当然一个结点可能同时依靠孩子、依靠双亲或依靠自己。
第二个阶段jjj表示结点u被哪个结点支配。 -
决策:是否选择结点
-
策略:选择结点的方案
-
策略集合:以结点u为根的子树中,被支配情况为jjj的情况下,所有选择结点的方案。
-
条件:点权加和最小
-
统计量:点权加和
状态定义:
dpu,jdp_{u,j}dpu,j:以结点uuu为根的子树中,被支配情况为jjj的情况下,选择结点的所有方案中,点权加和最小的方案的点权加和。
j=0j=0j=0表示不选择结点u,且结点u双亲被选择(依靠双亲)
j=1j=1j=1表示不选择结点u,且结点u的孩子被选择(依靠孩子)
j=2j=2j=2表示结点u自己被选择(依靠自己)
状态转移方程
- 策略集合:以结点u为根的子树中,被支配情况为jjj的情况下,所有选择结点的方案。
- 分割策略集合:对于u的各个子树,根据是否选择子树的根结点来分割策略集合。
在dfs过程中,从结点u访问到u的邻接点v
- 如果选择结点u,那么结点u的孩子v可以依靠自己,依靠双亲或依靠孩子,三种情况求最小值。对以每个以孩子v为根的子树求出其选择结点点权加和的最小值,进行加和。dpu,2=∑v∈childumin{dpv,0,dpv,1,dpv,2}dp_{u,2} = \underset{v\in child_u}\sum\min\{dp_{v,0}, dp_{v,1}, dp_{v,2}\}dpu,2=v∈childu∑min{dpv,0,dpv,1,dpv,2}
- 如果不选择结点u,且结点u依靠双亲,则结点u的孩子v只能依靠自己或依靠孩子,两种情况取最小值。对以每个以孩子v为根的子树求出其选择结点点权加和的最小值,进行加和。dpu,0=∑v∈childumin{dpv,1,dpv,2}dp_{u,0} = \underset{v\in child_u}\sum\min\{dp_{v,1}, dp_{v,2}\}dpu,0=v∈childu∑min{dpv,1,dpv,2}
- 如果不选择结点u,且结点u依靠孩子,则结点u的孩子v只能依靠自己或依靠孩子,两种情况取最小值。
即dpu,1=∑v∈childumin{dpv,1,dpv,2}dp_{u,1} = \underset{v\in child_u}\sum min\{dp_{v,1}, dp_{v,2}\}dpu,1=v∈childu∑min{dpv,1,dpv,2}
delta=∑v∈childumin{dpv,2−dpv,1}delta= \underset{v\in child_u}\sum min\{dp_{v,2}-dp_{v,1}\}delta=v∈childu∑min{dpv,2−dpv,1}
如果delta≤0delta \le 0delta≤0,则说明存在dpv,2<dpv,1dp_{v,2}<dp_{v,1}dpv,2<dpv,1,u存在孩子v选择依靠自己,那么u就可以依靠孩子。dpu,1dp_{u,1}dpu,1无需改变。
如果delta>0delta > 0delta>0,则说明u的所有孩子v都选择依靠孩子,u的所有孩子都没有被选择,这是不行的。需要将所有孩子中从“依靠孩子”变为“依靠自己”点权增加最小的结点,从“依靠孩子“变为”依靠自己“,其增加的点权为deltadeltadelta。此时dpu,1dp_{u,1}dpu,1的值需要再增加deltadeltadelta。
根结点为1,最终求根结点“依靠孩子”以及“依靠自己”两种情况下得到的点权加和的最小值,即为该树的最小支配集的大小。
【注意】在输入时,不可以写cin >> i >> w[i]
,代码无法先完成输入i
,再使用刚刚输入的i的值来确定w[i]
变量。必须分开写cin >> i; cin >> w[i]
,或者写cin >> i >> k; w[i] = k;
【题解代码】
解法1:树形动规 求树的最小支配集
#include<bits/stdc++.h>
using namespace std;
#define N 1505
int n, w[N], dp[N][3];//在以i为根的树中,dp[i][0]:不取i,i被父结点覆盖 dp[i][1]:不取i,i被子结点覆盖 dp[i][2]:取i,i被自己覆盖 的最小支配集的结点数
vector<int> edge[N];
void dfs(int u, int fa)
{dp[u][2] = w[u];int delta = 1e9;for(int v : edge[u]){if(v != fa){dfs(v, u);dp[u][0] += min(dp[v][1], dp[v][2]);//u被父结点覆盖时,子结点v可能被v的子结点覆盖,或被v自己覆盖 delta = min(delta, dp[v][2]-dp[v][1]);//需要增加的结点数的最小值为deltdp[u][1] += min(dp[v][1], dp[v][2]); dp[u][2] += min(min(dp[v][0], dp[v][1]), dp[v][2]);//如果u位置有结点,其孩子v可以被其父亲、孩子、自己覆盖 }}if(delta > 0)//如果delta<=0,说明求dp[u][1]时有取到dp[v][2],u可以依靠孩子。如果delta>0,说明u没有孩子可以依靠,将一个孩子从依靠孩子变为依靠自己增加的最小代价为delt dp[u][1] += delta;//得把其中 变为靠自己覆盖改变值最小的结点,改为靠v自己被覆盖
}
int main()
{int i, r, k, m;cin >> n;for(int j = 1; j <= n; ++j){cin >> i >> k >> m;w[i] = k;for(int h = 1; h <= m; ++h){cin >> r;edge[i].push_back(r);edge[r].push_back(i);}}dfs(1, 0); cout << min(dp[1][1], dp[1][2]);return 0;
}