结点覆盖
题目
解析
一个节点被覆盖只有三种可能:
设
d
p
x
,
0
/
1
/
2
设dp_{x,0/1/2}
设dpx,0/1/2分别表示被孩子/自己/父亲覆盖,可以得到转移方程:
d
p
x
,
0
=
m
i
n
(
d
p
s
o
n
x
,
1
−
m
i
n
(
d
p
s
o
n
x
,
1
,
d
p
s
o
n
x
,
0
)
)
+
∑
m
i
n
(
d
p
s
o
n
x
,
0
,
d
p
s
o
n
x
,
1
)
dp_{x,0}=min(dp_{son_x,1}-min(dp_{son_x,1},dp_{son_x,0}))+∑min(dp_{son_x,0},dp_{son_x,1})
dpx,0=min(dpsonx,1−min(dpsonx,1,dpsonx,0))+∑min(dpsonx,0,dpsonx,1)
(选一个1,其余选min(0,1))
d
p
x
,
1
=
w
x
+
∑
m
i
n
(
d
p
s
o
n
x
,
0
,
d
p
s
o
n
x
,
1
,
d
p
s
o
n
x
,
2
)
dp_{x,1}=w_x+∑min(dp_{son_x,0},dp_{son_x,1},dp_{son_x,2})
dpx,1=wx+∑min(dpsonx,0,dpsonx,1,dpsonx,2)
(子节点随便选)
d
p
x
,
2
=
∑
d
p
s
o
n
x
,
0
dp_{x,2}=∑dp_{son_x,0}
dpx,2=∑dpsonx,0
(显然它的父节点,它本身及它的子节点都没有选,故子节点只有0的状态)
最终答案即为
m
i
n
(
d
p
x
,
0
,
d
p
x
,
1
)
min(dp_{x,0},dp_{x,1})
min(dpx,0,dpx,1)(因为根没有父节点)
code:
#include<cstdio>
#define int long long
using namespace std;
inline int min(int x,int y){return x<y?x:y;}
int n,x,y,q,nxt[3010],to[3010],tot,head[1510],dp[1510][3],val[1510];
inline void add(int x,int y){to[++tot]=y,nxt[tot]=head[x],head[x]=tot;}
inline void dfs(int x,int fa)
{
register int mn=0;
dp[x][1]=val[x],dp[x][0]=1e9;
for(int i=head[x];i;i=nxt[i])if(to[i]!=fa)dfs(to[i],x),mn+=min(dp[to[i]][0],dp[to[i]][1]),dp[x][1]+=min(min(dp[to[i]][1],dp[to[i]][0]),dp[to[i]][2]),dp[x][2]+=dp[to[i]][0];
for(int i=head[x];i;i=nxt[i])if(to[i]!=fa)dp[x][0]=min(dp[x][0],mn-min(dp[to[i]][0],dp[to[i]][1])+dp[to[i]][1]);
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;++i)
{
scanf("%lld",&x),scanf("%lld%lld",&val[x],&q);
while(q--)scanf("%lld",&y),add(x,y),add(y,x);
}
dfs(1,0);
printf("%lld",min(dp[1][0],dp[1][1]));
return 0;
}