题意:给出一些关系,表示A能把病传染给B,这些关系形成树,每个周期疾病向下传染一层。每个周期开始的时候可以切断一个传染,初始只有1号带病,求最少患病的人数。1<=人数<=300。
开始想贪心或DP,无果,CS告诉我们这道题可以搜索搞定,大家一起研究了一下CS几年前迷一样的Pascal代码。
DFS挺简单,思维的障碍在于我局限于树形DP的模型,总想枚举一下每棵子树分别何时切断,没想到以层为单位搜索。
把整棵树横向建图,每层的结点按子树大小排序,方便进行最优性剪枝。直接DFS可能也行。vector挺方便。
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAX_N = 300;
vector<int> E[MAX_N+1], F[MAX_N+1];
int n, ans, best = 1<<30, maxd, sz[MAX_N+1], fa[MAX_N+1], dep[MAX_N+1];
bool b[MAX_N+1];
void build(int u, int p, int d)
{
F[d].push_back(u);
maxd = max(d, maxd);
sz[u] = 1;
fa[u] = p;
dep[u] = d;
for (int i = 0; i < E[u].size(); ++i) {
int v = E[u][i];
if (v != p) {
build(v, u, d+1);
sz[u] += sz[v];
}
}
}
bool cmp(int i, int j)
{
return sz[i] > sz[j];
}
void dfs(int d)
{
if (ans >= best)
return;
int cnt = 0;
for (int i = 0; i < F[d].size(); ++i) {
int u = F[d][i];
if (b[fa[u]])
++cnt, b[u] = true;
else
b[u] = false;
}
if (!cnt) {
best = min(ans, best);
return;
}
ans += cnt-1;
// 枚举d层哪个点不被感染
for (int i = 0; i < F[d].size(); ++i) {
int u = F[d][i];
if (b[u]) {
b[u] = false;
dfs(d+1);
b[u] = true;
}
}
ans -= cnt-1;
}
int main()
{
int p;
scanf("%d %d", &n, &p);
for (int i = 0; i < p; ++i) {
int u, v;
scanf("%d %d", &u, &v);
E[u].push_back(v);
E[v].push_back(u);
}
build(1, 0, 0);
for (int i = 1; i <= maxd; ++i)
sort(F[i].begin(), F[i].end(), cmp);
b[1] = true;
ans = 1;
dfs(1);
printf("%d\n", best);
return 0;
}