设f[i][j]表示以节点i为根的子树在状态j的情况下的最优解。
j有两种情况。
j=1:i这个根节点有士兵在站岗。
j=0:i这个根节点没有士兵在站岗。
转移方程很好想。
f[x][]+=min(f[to][],f[to][]);
f[x][]+=f[to][];
这样子。
意思就是:如果根节点已经有人站岗了,那么它的直接子节点可站可不站。就从子节点的两种状态中选一个小的。
如果根节点没有人站岗,那它的所有直接子节点都必须站岗,否则会有道路有人看不到。
代码奉上。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std; inline long long read(){
long long num=,f=;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-;
ch=getchar();
}
while(isdigit(ch)){
num=num*+ch-'';
ch=getchar();
}
return num*f;
} struct Edge{
int next,to;
}edge[];
int head[],num=;
inline void add(int from,int to){
edge[++num]=(Edge){ head[from],to};
head[from]=num;
} int f[][];
int size[]; void find(int x,int fa){
size[x]=;
for(int i=head[x];i;i=edge[i].next){
int to=edge[i].to;
if(to!=fa){
find(to,x);
size[x]+=size[to];
}
}
} void dfs(int x,int fa){
f[x][]=;f[x][]=;
if(size[x]==) return;
for(int i=head[x];i;i=edge[i].next){
int to=edge[i].to;
if(to!=fa){
dfs(to,x);
f[x][]+=min(f[to][],f[to][]);
f[x][]+=f[to][];
}
}
}
int ans=0x7fffffff;
int main(){
int n=read();
for(int i=;i<=n;++i){
int p=read(),s=read();
for(int j=;j<=s;++j){
int q=read();
add(p,q);
add(q,p);
}
} for(int i=;i<n;++i){
memset(f,,sizeof(f));
memset(size,,sizeof(size));
find(i,i);
dfs(i,i);
ans=min(ans,min(f[i][],f[i][]));
}
printf("%d",ans); return ;
}