水的一批的树型dp,看题没看清以为是用最少关键点覆盖所有点,结果是点覆盖所有边
那就是个很简单的问题了(虽然点覆盖点也不难,但我还是写了好久,WA了)
设 f 是 i 点不设防,f 是 i 点设防情况下以 i 为根的子树的最小贡献。
因为如果一个点不设防,那么它的所有儿子必须设防来覆盖父亲到其的那条边,
如果一个点设防,那它儿子设不设防就无所谓了,取最小贡献就 OK。
转移方程看代码吧。
#include <iostream> #include <stdlib.h> #include <string.h> #include <stdio.h> #define MAXN 1510 using namespace std; typedef long long LL; const int inf=0x3f3f3f3f; int n,f[MAXN][2]; int head[MAXN],to[MAXN*2],nxt[MAXN*2],tot=1; int read(){ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();} return x*f; } void add(int u,int v){to[++tot]=v;nxt[tot]=head[u];head[u]=tot;} void dfs(int u,int rt){ f[u][0]=0;f[u][1]=1; for(int i=head[u];i;i=nxt[i]){ if(to[i]==rt) continue; dfs(to[i],u); f[u][0]+=f[to[i]][1]; f[u][1]+=min(f[to[i]][1],f[to[i]][0]); } } int main(){ n=read(); for(int i=1,u,x;i<=n;i++){ u=read()+1;x=read(); for(int j=1,v;j<=x;j++) {v=read()+1;add(u,v);add(v,u);} } dfs(1,0); printf("%d\n",min(f[1][0],f[1][1])); return 0; }