传送门

水的一批的树型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;
}
01-05 14:29