树初步
数的基础内容可以看看树基础 - OI Wiki里面的讲解,对一些操作的基础概念介绍的很清楚;
下面直接来看例题:
题目大意
给定一个n+1个节点的有根数;
根节点(0号)是插座,额定功率2200;
叶子节点是用电器,有运行的实际功率;
其他的中间节点都是插排,也会标有额定功率;
跟实际一样我们的实际功率不可以大于额定功率;我们可以调换中间插座的位置,看能否使这个树正常运行;
思路分析
利用DFS去遍历树,计算在每个插排的点的实际功率;然后利用贪心去安排每个地方的插排;最后看能否满足每个位置的实际功率都不超过额定功率;
遍历时记录所有用电器的和,不能超过根的2200;
这里的贪心我们可以先把所有插排的额定功率都先存起来,然后再把每个插排位置上的实际功率也存起来;
之后对两个数组进行排序;这样两个数组中的数会是一一对应的,我们就把小的分给小的大的分给大的,也就是遍历比较每一位,观察有没有超出额定的情况;如果有则说明怎么换都不能满足要求;
具体的操作思路在下面代码的注释中都有讲解
代码实现
看的时候先看主函数再去看dfs遍历的函数
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define push push_back
const int N=1e5+5;
vector<int>q[N],aa,bb; // 建立的树q,存额定的aa,记录实际的bb
map<int,int>mp;// 用来标记一下插排是那几个节点
int a[N];// 记录每个节点的值
int ss=0;// 记录所有用电器的总和
int dfs(int x){// 这里的x是节点的编号(下标)
if(q[x].size()==0){// 遍历到叶子结点(用电器)
ss+=a[x];// 将这个值累加
return a[x];// 返回这个用电器的实际功率
}
int s=0;// 记录这个插排所连的点的功率和
for(int i=0;i<q[x].size();i++){
s+=dfs(q[x][i]);
}
bb.push(s);// 将这个插排处的实际功率记录到数组bb中
return s;
}
void slove(){
int n;cin>>n;
for(int i=1;i<=n;i++){
int u,v;cin>>u>>v;
a[i]=v;// 存值
q[u].push(i); //建树
mp[u]++;// 标记插排
}
for(auto i:mp){
aa.push(a[i.first]);// 遍历每个插排,将额定功率存起来
}
dfs(0);// DFS遍历树(0是插座是根节点所以从0开始遍历)
if(ss>2200){ // 所有的和不能超过2200
cout<<"NO";
return;
}
bb.push(0);// 因为aa会把0这个根节点存进去,所以我们把bb前面也放入0来对齐数位,比较后面插排的匹配情况
sort(aa.begin(),aa.end());
sort(bb.begin(),bb.end());
for(int i=0;i<aa.size();i++){
if(aa[i]<bb[i]){// 实际功率超过了额定功率就不可以
cout<<"NO";
return;
}
}
cout<<"YES";
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int _=1;
//cin>>_;
while(_--)
slove();
return 0;
}