P1041 传染病控制 解题报告

题目

题意简述

在一颗树中的每个深度找出一些不重合的子树,使所有子树的大小之和最大。

解题思路

直接暴力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;
}

  

猜你喜欢

转载自www.cnblogs.com/whx666/p/11344478.html
今日推荐