题意简述
在一颗树中的每个深度找出一些不重合的子树,使所有子树的大小之和最大。
解题思路
直接暴力dfs(枚举)方案,好像想不到特别有用的剪枝,就打了朴素的搜索,结果就过了(数据范围小)
先从根节点 dfs 遍历整棵树,把会用到的信息都记录下来(节点的深度,每个节点的子树大小)
为了后面搜索方案时方便,我还记录了每个深度的点集
因为子树不能重合,所以又开了一个数组记录当前可以选择作为根节点的点集,每次用上一个深度的可用点拓展出这个深度的可用点
避免子树重合的方法:不拓展已选节点的子树
数组变量声明
v 记录与每个节点有连边的点,sum记录以每个点为根节点的子树大小,deep记录节点深度,k记录每个深度的所有节点,q记录当前每个深度的可用节点。
代码
#include <bits/stdc++.h> using namespace std; const int N = 310; int n, m, a, b, maxn; int v[N][N], cntv[N], sum[N], deep[N], k[N][N], cntk[N], q[N][N], cntq[N]; inline void Go (int p, int fa, int d) { //遍历整棵树,记录信息 deep[p] = d, sum[p] = 1, k[d][++cntk[d]] = p; for (int i = 1; i <= cntv[p]; i++) if (v[p][i] != fa) Go (v[p][i], p, d + 1), sum[p] += sum[v[p][i]]; } inline void Do (int d, int s, int last) { //搜索方案 maxn = max (maxn, s); for (int i = 1; i <= cntq[d-1]; i++) if (q[d-1][i] != last) //如果这个点不是上一次选中点,拓展子节点为可用节点 for (int j = 1; j <= cntv[q[d-1][i]]; j++) if (deep[v[q[d-1][i]][j]] == d) q[d][++cntq[d]] = v[q[d-1][i]][j]; //if判断避免加入这个点的父节点 for (int i = 1; i <= cntq[d]; i++) Do (d + 1, s + sum[q[d][i]], q[d][i]), cntq[d+1] = 0; } int main() { scanf ("%d %d", &n, &m); for (int i = 1; i <= m; i++) { scanf ("%d %d", &a, &b); v[a][++cntv[a]] = b, v[b][++cntv[b]] = a; } q[1][1] = 1, cntq[1] = 1; Go (1, 0, 1), Do (2, 0, 0); printf ("%d", n - maxn); return 0; }