题目链接
树形DP
令 f u , 0 f_{u,0} fu,0表示点 u u u不放士兵时 u u u的子树(包括 u u u)最少需要多少个士兵, f u , 1 f_{u,1} fu,1表示点 u u u放士兵时 u u u的子树最少需要多少个士兵。
因为需要每条边的端点都有人,所以若点 u u u不放,则 u u u的每一个儿子都要放,否则 u u u与儿子的连边的两端将都没有士兵,不符合题意。记 s o n u son_u sonu为 u u u的儿子的集合,则转移方程为:
f u , 0 = ∑ v ∈ s o n u f v , 1 , f u , 1 = ( ∑ v ∈ s o n u m a x ( f v , 0 , f v , 1 ) ) + 1 f_{u,0} = \sum_{v \in son_u} f_{v,1},f_{u,1} = (\sum_{v \in son_u} max(f_{v,0},f_{v,1})) + 1 fu,0=v∈sonu∑fv,1,fu,1=(v∈sonu∑max(fv,0,fv,1))+1
最后答案为 m a x ( f r o o t , 0 , f r o o t , 1 ) max(f_{root,0},f_{root,1}) max(froot,0,froot,1)
#include
#include
using namespace std;
const int maxn = 1550;
int n,u,v,k,cnt,last[maxn],f[maxn][2];
struct Edge{
int v,nxt;
}e[2 * maxn];
int read(){
int x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + (c ^ 48),c = getchar();
return x;
}
void insert(int x,int y){
cnt ++,e[cnt].v = y,e[cnt].nxt = last[x],last[x] = cnt;
}
void dfs(int u,int fa){
f[u][1] = 1;
for(int i = last[u]; i; i = e[i].nxt){
int v = e[i].v;
if(v == fa) continue;
dfs(v,u);
f[u][0] += f[v][1],f[u][1] += min(f[v][0],f[v][1]);
}
}
int main(){
n = read();
for(int i = 1; i <= n; i ++){
u = read(),k = read();
for(int j = 1; j <= k; j ++) v = read(),insert(u,v),insert(v,u);
}
dfs(0,-1);//编号从0开始
cout << min(f[0][0],f[0][1]) << endl;
return 0;
}