树初步 #1(插排串联 - 辽宁省2024CCPC)

发布于:2025-05-10 ⋅ 阅读:(10) ⋅ 点赞:(0)

树初步

数的基础内容可以看看树基础 - OI Wiki里面的讲解,对一些操作的基础概念介绍的很清楚;

下面直接来看例题:

插排串联 - 辽宁省CCPC

题目大意

给定一个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;
}


网站公告

今日签到

点亮在社区的每一天
去签到