P3915树的分解

 题意:将一棵树(不一定是二叉树)分割成每部分的大小都是K的子树,看看是否能够分割成功,成功输出YES,不成功输出NO

由于分割成子树处理,可以遐想到递归的处理,分割一个树,先去分割他的子树,再分割子树的子树...本题如果从自顶而下做比较困难,使用深度优先处理(dfs),可以递归到子树再开始处理。

 

 

 

递归到最小子树,就开始返回,他的值-》一直到返回到最初的dfs,再返回到main函数

题目中的题意告诉我们是否能分割成多个大小为K的子树,

所以某一个子树的子树+它本身>K,就说明本题的答案已经是NO了,直接return-1即可

而在子树返回的数==K时,我们已经知道这个子树可以被分割成一个符合题意的子树了,就不用再计数这棵树,直接当成不存在就好了,所以直接+0

返回数小于k的时候,说明这棵树的总结点数还没有到达要求,我们让他继续加下去

可以得到一个基本模板

ANS=1;
for(一棵树的多个子树编号)
{
    int t=弹出的子树编号;
    if(子树编号==现在这棵树的母结点)
        continue;            //直接跳过
    //now表示现在的子树编号
    int z=dfs(弹出的子树编号,now);
    if(z<K);        //z<K不管他
    if(z==K) z=0;        //z=k,让他为0
    if(z==-1||z>k)
        return -1;       //返回-1,表示这个树已经不能被分割成题意了
    ANS+=z;             //ANS表示现在这棵树的节点个数(包括它本身)
}
return ANS;

 以下是源代码

#include <iostream>
#include <vector>                    //动态数组用来存储,防止空间浪费
using namespace std;

//int s[100005];					//存储树的数组
int N;							//结点数
int K;							//连通块大小
vector<int> s[100005];				//存储树的数组
int ans[10] = { 0 };
int dfs(int now, int last)
{
	int ANS = 1;
	for (int i = 0;i < s[now].size();i++)				//链表中所有的点
	{
		int t = s[now][i];						//弹出的第i个结点
		if (t == last)
			continue;							//是母结点,直接跳过
		int k = dfs(t, now);
		/*k<K   继续加
		k==k    让k变回为0
		k>K     返回-1*/
		if (k == K) k = 0;
		if (k == -1||k>K) return -1;
		ANS += k;
	}
	return ANS;
}
int main()
{
	int t,v1,v2,i=0;
	cin >> t;
	int j = t;
	while (t--)
	{
		cin >> N >> K;
		for (int i = 0;i <= N ;i++)
			s[i].clear();
		for (int i = 0;i < N - 1;i++)
		{
			cin >> v1 >> v2;
			s[v1].push_back(v2);		//存入子结点
			s[v2].push_back(v1);		//存入母结点(本题没有用处,但其他题求路径用得到)
		}
		ans[i] = dfs(1, 1);
		if (ans[i] == -1 || ans[i] > K)  ans[i] = 0;
		else ans[i] = 1;
		i++;
	}
	for (int i = 0;i < j;i++)
		if (ans[i] == 0)
			cout << "NO" << endl;
		else
			cout << "YES" << endl;
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值