思路:
题目的主要信息:
- n个房间,n-1条通道连通,这就是一棵树
- 树每个节点值记录在x数组
- 去掉树的一些边,使之成为k个子树,且每个子树的节点值和大于等于m,问可行性
方法一:dfs
具体做法:
主体思路是,将树分成每个部分刚好大于等于m,看是否有大于等于k个子树。
首先构建图,利用深度优先搜索自底向上将每个子树的节点值往上累加,每次遇到累加和大于等于m时,往上加的就归零,相当于切断该子树往上的边,同时计数k减一,最后检查k是否小于等于0即可。
class Solution { public: //深度优先搜索函数相加和大于等于m即k减一 int dfs(vector<vector<int>>& G, vector<int>& x, int n, int& k, int m, int u, int pre){ if(k <= 0) //达成目标 return 0; int sum = x[u - 1]; //节点值相加和 for(int i = 0; i < G[u].size(); i++) //对相邻的每个节点深度优先搜索 if(G[u][i] != pre) sum += dfs(G, x, n, k, m, G[u][i], u); if(sum >= m){ //满足条件 k--; return 0; } return sum; } bool solve(int n, int k, int m, vector<int>& u, vector<int>& v, vector<int>& x) { vector<vector<int> > G(n + 1); for(int i = 0; i < u.size(); i++){ //构建图 G[u[i]].push_back(v[i]); G[v[i]].push_back(u[i]); } dfs(G, x, n, k, m, 1, -1); return (k <= 0); } };
复杂度分析:
- 时间复杂度:,dfs遍历树的每个节点
- 空间复杂度:,构建图的辅助数组G及递归栈深度
方法二:bfs
具体做法:
同样是方法一的思想,但是累加的和却用bfs实现。
我们利用degree数组记录每个节点的出度,因为我们构建的图是无向图,因此叶子节点出度为1,找到所有出度为1的点入队列,我们每次从叶子节点出发累加子树的节点值(类似方法一的自底向上加),即辅助数组y记录的是每个子树的累加节点和,一开始都为自己节点值。如果该子树节点和已经是大于等于m,我们可以像方法一一样,断掉该边,往旁边相连的都是加0,计数k减一;否则需要像旁边所有的辐射累加。
class Solution { public: bool solve(int n, int k, int m, vector<int>& u, vector<int>& v, vector<int>& x) { vector<vector<int> > G(n + 1); vector<int> degree(n + 1, 0); for(int i = 0; i < u.size(); i++){ //构建图 G[u[i]].push_back(v[i]); G[v[i]].push_back(u[i]); degree[u[i]]++; //每个节点度数 degree[v[i]]++; } vector<bool> vis(n + 1, false); queue<int> q; vector<int> y = x; //记录每棵子树的金币数 for(int i = 0; i < n; i++){//所有度数为1的节点即叶子节点入队 if(degree[i] == 1){ q.push(i); vis[i] = true; } } while(!q.empty()){ //bfs int temp = q.front(); q.pop(); if(y[temp - 1] >= m){ //该子树数量大于m,计数并子树归零,代表截断 k--; if(k <= 0) //够了满足条件 break; y[temp - 1] = 0; } for(int i = 0; i < G[temp].size(); i++){ //广度优先 y[G[temp][i] - 1] += y[temp - 1]; //与该子树邻接的节点都要加上该子树的 if(!vis[G[temp][i]]){ vis[G[temp][i]] = true; q.push(G[temp][i]); } } } return k <= 0; } };
复杂度分析:
- 时间复杂度:,多次遍历,最多也都是遍历树的每个节点
- 空间复杂度:,辅助队列、各个辅助数组的长度