(\)
Description
给出一棵 (n) 个节点以 (1) 为根的树,一个节点的覆盖半径是 (1) ,点有点权 (val_x) 。
选择一些点,使得点权和最小,同时每个节点要么被选择要么被周围的点覆盖。
- (nle 1500,0le val_xle 10^4)
(\)
Solution
树形DP 的讨论。
注意到覆盖有可能呈现出两层都没有选点的情况 (下面被子树覆盖,上面被父节点覆盖),所以状态设计要注意。
设(f[i][0/1/2]),表示节点 (i) 及其子树的覆盖代价,明确定义:
- (0) 表示选择自己,覆盖整个子树的最小代价
- (1) 表示第 (i) 个节点被自己的子树的根节点覆盖,不选择自己的最小代价
- (2) 表示第 (i) 个节点被父节点覆盖,此时当前节点的所有子树都已经完成覆盖的最小代价
转移讨论起来就很方便了。
(0) :显然要选自己,所以所有子树选什么都合法,对每个子树累加 (min(f[v][0],f[v][1],f[v][2])) 。
(2):不选自己,子树内部显然不能再向当前点提出需求,所以对子树累加 (min(f[v][0],f[v][1])) 。
(1) 的转移有点意思。
如果我们贪心的选,选择 (0) 状态最小的子树,剩下的子树都选 (1) 状态,不一定是最优的。
因为这个 (0) 状态最小的子树,他的 (1) 状态可能会更小的多,这个差值完全能够允许另一个 (1) 状态变成 (0) 状态。
(\)
所以考虑替换, (yy) 出来一个比较好的写法。
先对所有子树求出 (sum=min(f[v][0],f[v][1])) 。
同时维护 (tmp=min( f[v][0]-min(f[v][0],f[v][1]) ))。
(\)
这个 (sum) 的含义是,不考虑子树覆盖当前节点, 子树内部覆盖的最小值,可以发现其实就是 (f[u][2]) 。
(tmp) 的含义就是,把这个 (sum) 集合里的任意一个点不管之前选的什么,现在变成 (0) 状态的最小代价。
如果之前求 (sum) 的时候选了一个 (0) 状态,那么这个 (tmp) 显然是 (0) 。
如果之前没有选到任意一个 (0) 状态,那么这个 (tmp) 就是所有的 (1) 状态里,变成 (0) 状态的最小代价。
所以有 (f[v][1]=f[v][2]+tmp) 。
(\)
Code
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1510
#define gc getchar
#define R register
#define inf 2000000000
using namespace std;
inline int rd(){
int x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
int n,m,tot,hd[N],f[N][3],val[N];
struct edge{int to,nxt;}e[N<<1];
inline void add(int u,int v){
e[++tot].to=v; e[tot].nxt=hd[u]; hd[u]=tot;
}
//0: 选自己
//1: 选子树内覆盖自己
//2:选父节点覆盖自己
void dfs(int u,int fa){
int mn=inf;
f[u][0]=val[u]; f[u][2]=0;
for(R int i=hd[u],v;i;i=e[i].nxt)
if((v=e[i].to)!=fa){
dfs(v,u);
f[u][0]+=min(f[v][2],min(f[v][1],f[v][0]));
f[u][2]+=min(f[v][1],f[v][0]);
mn=min(mn,f[v][0]-min(f[v][1],f[v][0]));
}
f[u][1]=f[u][2]+mn;
}
int main(){
n=rd();
memset(f,0x3f,sizeof(f));
for(R int i=1,u,cnt;i<=n;++i){
u=rd(); val[u]=rd(); cnt=rd();
for(R int j=1,v;j<=cnt;++j){v=rd();add(u,v);add(v,u);}
}
dfs(1,0);
printf("%d
",min(f[1][0],f[1][1]));
return 0;
}