题意分析
- 给你一棵树,需要我们将这棵树划分为k个联通块,同时每个联通块的节点的权值的和都要大于等于给定的值,问是否可以达到目的。
思路分析
我们观察得到,为了确保是联通块,而且达到贪心的目的,我们应该从树的叶子节点往上面进行扩展。如果某一个节点和他的子树可以构成一个满足题目条件的联通块。那么我们就将这棵树和他的结点的权值都置为0.继续向上进行扩展。这是一个树形DP的思想。当然,也可以用BFS进行处理。
如下图
解法一 DFS(DP)
根据上面的分析,我们可以很好的写出这个DP函数,首先,从根节点开始进入,然后,处理字节点,同时判断此时的子节点是否可以构成一个满足条件的联通块。可以就将权值都置为0.
代码如下
- 需要遍历所有的结点以及他们的连边。所以,时间复杂度为
- 需要存储所有的边的情况以及权值情况。所以空间复杂度为
- 需要遍历所有的结点以及他们的连边。所以,时间复杂度为
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型
* @param k int整型
* @param m int整型
* @param u int整型vector
* @param v int整型vector
* @param x int整型vector
* @return bool布尔型
*/
vector<int> node[200010];
int sum[200010];
int res=0;
// 树形DP
void dp(int x,int fa,int m,vector<int>& tmp){
sum[x]=tmp[x-1];
for(auto y:node[x]){
// 遍历所有的连边的结点
if(y==fa) continue;
dp(y,x,m,tmp);
sum[x]+=sum[y];
}
// 如果最后这个结点和它的子树构成的金币的和大于等于m,那么就将这一块进行合并
if(sum[x]>=m){
sum[x]=0;
res++;
}
}
bool solve(int n, int k, int m, vector<int>& u, vector<int>& v, vector<int>& x) {
// write code here
// 存储这颗树
for(int i=0;i<n-1;i++){
node[v[i]].push_back(u[i]);
node[u[i]].push_back(v[i]);
}
dp(1,0,m,x);
return res>=k;
}
};解法二 BFS
我们上面知道,我们需要从叶子节点开始向上进行扩展。那么我们如何知道叶子节点在什么位置呢?有一个好的方法是计算每个节点的入度,入度为1的就说明是叶子节点了。然后,我们将这些节点放到队列里面,不断出队往根节点的方向进行扩展。同时维护满足条件的联通块的个数。
代码如下
- 队列可能会存储n个节点的情况,需要处理n个节点的情况,时间复杂度为
.
- 队列存储了n个节点的情况,而且会使用到一些数组维护权值的情况,空间复杂度为
- 队列可能会存储n个节点的情况,需要处理n个节点的情况,时间复杂度为
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型
* @param k int整型
* @param m int整型
* @param u int整型vector
* @param v int整型vector
* @param x int整型vector
* @return bool布尔型
*/
vector<int> node[200010];
int sum[200010],in[200010];
bool vis[200010]={false};
bool solve(int n, int k, int m, vector<int>& u, vector<int>& v, vector<int>& x) {
// write code here
// 存储这颗树
for(int i=0;i<n-1;i++){
node[v[i]].push_back(u[i]);
in[v[i]]++;
node[u[i]].push_back(v[i]);
in[u[i]]++;
}
queue<int> q;
// 先把叶子结点放到放进队列里面,从树的底部一层一层扩展到根结点
for(int i=1;i<=n;i++){
sum[i]=x[i-1];
if(in[i]==1){
q.push(i);
vis[i]=true;
}
}
int res=0;
while(!q.empty()){
int now=q.front();
q.pop();
// 如果满足题目的条件,那么就执行合并的操作
if(sum[now]>=m){
res++;
sum[now]=0;
}
for(auto y:node[now]){
// 父节点合并子节点的权值
sum[y]+=sum[now];
// 如果这个结点没有被合并过
if(!vis[y]){
vis[y]=true;
q.push(y);
}
}
}
return res>=k;
}
};
京公网安备 11010502036488号