CSP第25次 202203-3 计算资源调度器题解

试题编号: 202203-3
试题名称: 计算资源调度器
时间限制: 1.0s
内存限制: 512.0MB
题目链接:CSP第25次 CSP-CCF-202203-3 计算资源调度器

问题描述
题目背景
西西艾弗岛上兴建了一批数据中心,建设了云计算资源平台。小 C 是主管西西艾弗云开发的工程师。西西艾弗云中有大量的计算节点,每个计算节点都有唯一编号。
西西艾弗云分为多个可用区,每个计算节点位于一个特定的可用区。一个可用区中可以有多个计算节点。
西西艾弗云中运行的计算任务分为不同的应用,每个计算任务都有一个应用与之对应,一个应用中可能包括多个计算任务。
每个计算任务由一个特定的计算节点执行,下文中计算任务“运行在某可用区上”意即“运行在某可用区的计算节点上”。
不同的计算任务对运行的计算节点的要求不尽相同。有的计算任务需要在指定可用区上运行,有的计算任务要和其它应用的计算任务在同一个可用区上运行,
还有的希望不要和某个应用的计算任务在同一个计算节点上运行。
对于一个计算任务,执行它的计算节点一旦选定便不再更改;在选定计算节点后,该任务对计算节点的要求就不再被考虑,
即使新安排的计算任务使得此前已有的计算任务的要求被违反,也是合法的。
下图示意性地说明了可用区、计算节点、计算任务之间的关系,同时也说明了应用和计算任务的对应关系。
在这里插入图片描述
一开始,小 C 使用了电子表格程序来统计计算任务的分配情况。随着云上的计算节点和计算任务的不断增多,小 C 被这些奇怪的要求搞得焦头烂额,有的时候还弄错了安排,让客户很不满意。
小 C 找到你,希望你能为他提供一个程序,能够输入要运行的计算任务和对节点的要求,结合西西艾弗云的现有计算节点信息,计算出计算任务应该被安排在哪个计算节点上。
问题描述
计算任务对计算节点的要求十分复杂而且又不好描述,你对小 C 表示写程序这件事很为难。于是,小 C 进行了调研,
将这些需求进行了归纳整理,形成了下面这三种标准需求。在提出需求时,必须从这三种标准需求中选取若干种,每种需求只能有一条。选取多种需求意味着要同时满足这些需求。

  • 计算节点亲和性
    计算任务必须在指定可用区上运行。
  • 计算任务亲和性
    计算任务必须和指定应用的计算任务在同一可用区上运行。
    该要求对计算任务可以运行的可用区添加了限制。不考虑该任务本身,一个可用区若运行有指定应用的任务,则满足要求。
  • 计算任务反亲和性
    计算任务不能和指定应用的计算任务在同一个计算节点上运行。
    该要求对计算任务可以运行的计算节点添加了限制。不考虑该任务本身,一个计算节点若运行有指定应用的任务,则不满足要求。
    当要执行的计算任务多起来,计算任务反亲和性的要求可能很难满足。因此在添加计算任务反亲和性要求时,还要指定这个要求是“必须满足”还是“尽量满足”。

小 C 要求你按照如下方法来分配计算节点:按照计算任务的启动顺序,根据要求,依次为每个计算任务选择计算节点。一旦选择了一个计算节点,就固定下来不再变动,
并且在此后的选择中,不再考虑这个计算任务的要求。对每个计算任务,选择计算节点的方法是:

  1. 过滤阶段
    在这个阶段,先根据计算任务的要求,过滤出所有满足要求的计算节点。如果不存在这样的计算节点,并且指定了计算任务反亲和性要求,并且计算任务反亲和性要求是尽量满足的,
    那么去掉计算任务反亲和性要求,再过滤一次。如果还不存在,就认为该计算任务的要求无法满足,该计算任务无法分配。

  2. 排序阶段
    在这个阶段,将过滤后的计算节点按照这个方法排序:

    1. 选择此时运行计算任务数量最少的计算节点;
    2. 选择编号最小的计算节点。

输入格式
在这里插入图片描述
输出格式
在这里插入图片描述
样例输入

10 4
1 1 1 1 1 2 2 2 2 2
6
2 1 4 1 2 1
6 1 1 0 1 1
1 2 2 0 0 0
6 1 2 0 2 1
5 2 2 0 1 0
11 3 0 1 3 0

样例输出

0 0
1 2 3 4 5 0
6
7 8 9 10 7 8
6 6 6 6 6
1 2 3 4 5 9 10 7 8 6 1

样例解释
本输入中声明了十个计算节点,前五个位于可用区 1,后五个位于可用区 2。可用区 3 和 4 不包含任何计算节点。

对于第一组计算任务,由于它们声明了计算节点亲和性要求,但要求的可用区编号是 4,该可用区不包含计算节点,因此都不能满足。

对于第二组计算任务,要在可用区 1 中启动 6 份应用 1 的任务,并且要求了计算任务反亲和性。因此,前五份任务分别被安排在前五个节点上。对于第六份任务,由于它必须运行于可用区 1,所以能够安排的范围仅限于前五个节点。但是它还指定了强制的计算任务反亲和性,前五个节点上已经启动了属于应用 1 的计算任务,因此没有能够运行它的节点。

对于第三组计算任务,要在可用区 2 中启动 1 份应用 2 的任务,直接将其分配给节点 6。

对于第四组计算任务,要在可用区 2 中启动 6 份应用 1 的任务,并且要求了计算任务反亲和性,不能和应用 2 的计算任务分配在同一个节点上。因此,节点 6 不能用于分配,这六份任务只能分配在节点 7~10 上。按照题意,选取运行任务数最少的和编号最小的,因此依次分配 7、8、9、10、7、8。

对于第五组计算任务,要在可用区 2 中启动 5 份应用 2 的任务,并且要求了尽量满足的计算任务反亲和性,不能和应用 1 的计算任务分配在同一个节点上。此时,可用区 2 中的节点 6 上没有应用 1 的计算任务,因此这 5 份计算任务都会被分配到这个节点上。

对于第六组计算任务,要启动 11 份应用 3 的任务,并且要求了尽量满足的计算任务反亲和性,不能和应用 3 的其它计算任务分配在同一个节点上,同时要求和应用 1 的计算任务位于同一个可用区。应用 1 位于两个可用区,因此全部 10 个节点都可以用于分配。对于前 10 份任务,按照题意,依次选取运行的任务数最少且编号最小的节点进行分配。对于第 11 份任务,由于所有的节点上都运行有应用 3 的任务,因此没有节点符合它的反亲和性要求。又因为反亲和性要求是尽量满足的,因此可以忽略这一要求,将它安排在节点 1 上。

子任务
在这里插入图片描述

AC代码(C++)

在这里插入图片描述

Dev-CPP(C++14标准)

#include<bits/stdc++.h>
using namespace std;
//本地调试时使用
//#define inline 

typedef struct node{
	int id;//编号 
	int block;//区
	int nums;//当前的任务数 
	set<int> task; //所运行任务的编号 
	
	bool operator <(const node &a)const{
		if(this->nums!=a.nums){
			return this->nums<a.nums;
		}
		else {
			return this->id<a.id;
		}
	}
}node;

unordered_map<int,node> Node;

//编号为n的可用区内的计算节点
void fax1(int n,vector<int> &temp){
	for(auto it=temp.begin();it!=temp.end();){
		if(Node[*it].block!=n){
			temp.erase(it);
		}
		else it++;
	}
	return;
}

//和编号为n的应用的计算任务在同一个可用区
void fax2(int n,vector<int> &temp){
	set<int> qu; 
	for(auto it=Node.begin();it!=Node.end();it++){
		if(it->second.task.count(n)==1){
			qu.insert(it->second.block);
		}
	}
	for(auto it=temp.begin();it!=temp.end();){
		if(qu.count(Node[*it].block)==0){
			temp.erase(it);
		}
		else it++;
	}
	return;
}

//表示不能和编号为n的应用的计算任务在同一个计算节点上运行
vector<int> fax3(int n,vector<int> temp){
	for(auto it=temp.begin();it!=temp.end();){
		if(Node[*it].task.count(n)==1){
			temp.erase(it);
		}
		else it++;
	}
	return temp;
}

int main(){
#ifdef inline
	freopen("stdin.txt","r",stdin);
#endif
	ios::sync_with_stdio(false);
	int n,m;cin>>n>>m; //n个节点,m个区
	int i=1;
	
	vector<int> idsum;
	while(i<=n){
		int temp;
		cin>>temp;
		Node.insert(pair<int,node>(i,node{i,temp,0,set<int>()}));
		idsum.push_back(i);
		i++;
	}
// #ifdef inline
// 	for(auto &i:Node){
// 		cout<<i.first<<" "<<i.second.block<<endl;
// 	}
// 	cout<<"*************"<<endl;
// #endif
	
	
	int T;cin>>T;
	while(T--){
		int a1,a2,a3,a4,a5,a6;
		cin>>a1>>a2>>a3>>a4>>a5>>a6;
		
// #ifdef inline
// 	for(auto &i:temp){
// 		cout<<i<<" ";
// 	}
// 	cout<<"*************"<<endl;
// #endif
		
		while(a1--){
			vector<int> temp=idsum,temp2;
			//计算节点亲和性
			if(a3!=0) {
				fax1(a3,temp);
			}
			//计算任务亲和性
			if(a4!=0) {
				fax2(a4,temp);
			}
			//计算任务反亲和性
			if(a5!=0) {
				temp2=fax3(a5,temp);
			}
			//计算任务反亲和性要求是必须满足的且无可用节点   或 不考虑 计算任务反亲和性也无可用节点 
			if((temp2.empty()&&a6==1)||temp.empty()) {
				cout<<0<<" ";
				continue;
			}
			//计算任务反亲和性要求是尽量满足的 且 无可用节点   则 不考虑 计算任务反亲和性 
			else if(temp2.empty()&&a6==0){
				temp2=temp;
			}
			//排序 按照节点的已有的作业数、序号 
			sort(temp2.begin(),temp2.end(),[](const auto &a,const auto &b)->bool{
				return Node[a]<Node[b];
			});
			//更新节点信息 
			Node[temp2[0]].nums++;
			Node[temp2[0]].task.insert(a2);
			cout<<Node[temp2[0]].id<<" ";


		}
		cout<<endl;
		
	}
	return 0;
}

总结

没有用到什么算法,直接模拟就可以。用到的C++stl内容比较多,考验基础的代码能力和对stl的运用。

如果有哪里不懂可以直接评论,看到就会回复的。

  • 29
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值