题意
给一棵无根树,可将其中一些节点选为服务节点,每个服务节点可以服务其相邻节点。要求每个节点只被服务一次或者被选为服务节点,求满足要求的服务节点数量最少。
题解
本题前后用时2h,回头来看其实也是一道简单的树状dp。
在做本题时,自己的想法与紫书上的不谋而合。开始考虑时想直接借鉴最大独立集的状态定义(d(i)表示i子树的答案),但之后发现如果这样定义,那么在转移中就无法确认转移合法(满足题目要求)。通过分析要求发现,子树根节点i是否选作服务节点是关键问题。参考之前的经验,考虑用两个状态变量定义状态。在确定完状态,开始写状态转移方程时发现,这样定义状态会导致漏解。于是再维护一个值。
。。。关于状态和转移的东西日后在写。
代码
#include <cstdio>
#include <queue>
#include <cstring>
#include <vector>
#include <functional>
#include <algorithm>
#include <unordered_map>
#include <string>
#include <iostream>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 10000+1;
vector<int> G[maxn];
int d[maxn][2], f[maxn];
int n;
void dfs(int u, int fa){
if(G[u].size()==1 && G[u][0] == fa){
d[u][1] = 1;
f[u] = 0;
d[u][0] = maxn;
return;
}
d[u][1] = 1;
f[u] = 0;
for(int i = 0; i<G[u].size(); i++)if(G[u][i] != fa){
int v = G[u][i];
dfs(v, u);
d[u][1] += min(f[v], d[v][1]);
f[u] += d[v][0];
}
d[u][0] = INF;
for(int i = 0; i<G[u].size(); i++)if(G[u][i] != fa){
int v = G[u][i];
d[u][0] = min(d[u][0], f[u]-d[v][0]+d[v][1]);
}
}
int main(){
while(scanf("%d", &n) != EOF){
for(int i = 1; i<=n; i++) G[i].clear();
int u,v;
for(int i = 1; i<n; i++){
scanf("%d%d",&u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
int fg;
scanf("%d", &fg);
dfs(1, -1);
printf("%d\n", min(d[1][1], d[1][0]));
if(fg == -1) return 0;
}
}