2021华为软件精英挑战赛训练赛、正式赛思路分享
有幸再次参加了华为软件精英挑战赛(去年由于不知道数据集有坑,导致没能进入复赛,今年决定再来一次弥补去年的遗憾)
今年的赛题相比去年个人感觉还是好了一些的,从任务指导书所给的评分规则来看,确实要比去年单一的按程序运行时间来评判要好一些。并且题目属于开放性赛题,没有唯一的标准答案,所以在逻辑思维上可以很好的区分参赛选手(至少我是这么认为的)。具体的赛题可以到比赛官网中进行下载,或者点击这个链接初赛赛题下载
说说我们队伍的情况吧,我们是武长赛区正式赛排行榜中的第七名。
但是因为队友题目出来当晚,官方没有禁止开源代码的情况下,开源了一份baseline(只开源了一次),那份baseline没有加任何迁移算法,只是一个很基础的供选手参赛的代码,在训练赛中也只是50e的分数,(参赛的同学应该知道50e的分数是一个什么水平,就是垫底的分数)在大赛继续进行到12号的时候,因为开源代码的选手零零星星出现了不少,官方禁止了开源,我们也没有对外透漏过任何代码,在正式赛结束之后,很不幸,被查重了,我很呵呵,我加了迁移的算法怎么与其他人去冲突????查重不应该是在逻辑上查重吗?难道有人用了我的代码,自动的在我们的思路带领下,和我们写了一样的调度算法????很迷惑,申诉之后,官方的回信也的内容只有大赛的四条比赛规则(感觉只是走了一个流程),而且我们知道的其他一些开源的队伍,竟然没有被查到,更加迷惑了。我大三下了,为了这个比赛放弃了一段时间的考研复习,就是希望在这个赛事中找回去年的不爽,结果再次寒心……。吃一堑长一智吧。
说点开心的吧,在正式赛中团队整体奋斗的是我和另外一位老哥,老哥实力很强,在开始冲的还是很猛的,后期因为一些其他的事情,没有把太多精力放在这个上面,因为进复赛很稳了,但是没想到,呵呵呵。当然在这次比赛中,还认识了其他不少的大佬,比赛嘛,最开心的是认识很多朋友了。
赛题分析
在分析赛提前提醒大家,想要尽快写baseline、出结果、上分,一定要先读题,认认真真读题,好好分析赛题。
题目大致是给你一些服务器和虚拟机的类型,服务器分为两个节点A、B,服务器拥有的资源(CPU 和内存)均匀分布在这两个节点上。这句话是重点!!!这句话是重点!!!这句话是重点!!!也就意味着,如果服务器的类型为NV603 ,其CPU为92C,内存为324G,那么其 A、B 两个节点分别包含的资源为:CPU核数:46C 和,内存大小162G 的资源。并且保证服务器的CPU 核数和内存大小均为偶数。并且在题目中还给出了服务器的硬件成本以及每日耗能成本。
题目中所给的虚拟机有两种部署方式,分别为单双节点部署,单节点部署指的是一台虚拟机所需的资源(CPU和内存)完全由主机上的一个节点提供;双节点部署指的是一台虚拟机所需的资源(CPU 和内存)必须由一台服务器的两个节点同时提供,并且每个节点提供总需求资源的一半。
赛题要求根据所给的请求序列,创建服务器,部署虚拟机,或者按照用户请求在对应的服务器上删除相应的虚拟机。但要注意,服务器上的任意一个节点(A和 B)上的资源负载(CPU 和内存)均不能超过其容量上限。在完成每一天的服务器的扩容之后,在处理每一天的新请求之前,你还可以对当前存量虚拟机进行一次迁移,即把虚拟机从一台服务器迁移至另一台服务器。对于单节点部署的虚拟机,将其从一台服务器的 A 节点迁移至 B 节点(或反之)也是允许的。但迁移的虚拟机总量不超过当前存量虚拟机数量的千分之五。
通读赛题所有要求之后,其实我们可以发现这是一个类似于装箱的问题,把服务器比作箱子,把虚拟机比作需要放进去的货物。在一般的装箱问题中,我们首先要考虑的就是如何选择箱子,即选择用什么样的箱子来装什么样的货物是最合理的。这就需要涉及到对所有的箱子进行特征上的分析。接下来就是我们队伍对于整个赛题的思路(谨代表团队思路,如果讲解有误,勿喷!)
购买服务器思路
首先最开始我们想到的当然就是如何购买服务器,买容量最大的?买价格最便宜的?买CPU和内存大小最接近的?……上面的这几种思路并不没有一个标准的正确,首先在这种调度问题中,是没有一个正确的答案的,通过这些思路最终得到的结果是好是坏,很大一部分是取决于数据集的,但是我们也不能因为没有正确的答案,而且五花八门的胡乱猜测。首先,我们继续回到题目背景中去,背景中有如下一句话:
众所周知,这种比赛的目的一方面是为了选拔优秀选手,另一方面也是为了给自己公司当前所存在的问题,在民间寻找解决方法,可能你的思路在这个比赛中节约了一点点成本,放在真正的市场中,可以让华为的成本节约亿点点。
我们队伍在题目中所给数据中的服务器的规格进行了一个粗略的分析,服务器的种类比较丰富,每种服务器上CPU和内存的大小也不同,我们按照以下的几种方式对服务器进行了排序,并在每种排序后面写了我们的思考依据:
- CPU+内存:这样为了保证可以选择资源足够大的服务器
- 每日的耗能:如果请求天数足够多,服务器开启时间足够长,那么每日耗能的费用要远远大于其硬件成本
- CPU * 0.75 + 内存 * 0.25:魔法数字,后面还有部分也会用到,对于我们版本的代码来说,很重要!!!。
- 服务器的硬件成本:在请求天数较小,每天请求数量较小的情况下, 硬件成本所占的费用比重较大
- CPU:仅作为参考
- 内存:仅作为参考
- abs(CPU - 内存):大规模虚拟机的部署情况下,所有虚拟机所需的内存和CPU总和是接近的或者会趋近于某一个比值,为了让服务器可以部署更多的虚拟机,我们就尽量使服务器的CPU和内存大小接近,
- 服务器的硬件成本 / (CPU + 内存):考虑服务器的性价比。
处理请求思路
本题中的数据输入是一次性输入的,我们可以先把所有的数据保存下来,然后去对这些信息集中处理,这样我们就可以用一个上帝视角来解决这个问题,也可以按照每一条请求进行中规中距的进行处理(因为我们队伍依次对请求进行处理的分数还是比较客观的,又因为是初赛,也没有去花太多功夫去更改已经写好的baseline,所以没有采用上面看似比较良好的上帝视角去处理)在得到每一次的请求之后,判断其为“add”,还是“del”,依次进行请求处理。
选择服务器
在刚开始遇到“add”请求,或者在当前已有服务器无法满足请求中虚拟机所需要的资源时,我们需要重新开启新的服务器,在这时我们就会遇到服务器的选择问题。我们队伍根据上面的选择思路以及实验中得到结果进行对比后,采用的是第二种排序方式,然后对排好序的服务器从前到后依次遍历,选择刚好符合该虚拟机的服务器,具体代码如下:
int ii;
for (ii = 0; ii < model_pair_size; ii++)
{
//找到一个最合适虚拟机的服务器,vv代表的是虚拟机信息的结构体
if (vv.cpu < model_pair[ii].first.cpu / 2 && vv.Memory < model_pair[ii].first.Memory / 2 && !vv.Is_Double_node)
{
break;
}
if (vv.cpu < model_pair[ii].first.cpu && vv.Memory < model_pair[ii].first.Memory && vv.Is_Double_node)
{
break;
}
}
server ss = model_pair[ii].first;
在我们初赛的代码中,其他排序得到的结果没有这种排序的方式费用低,并且在线下数据集和正式赛中,(3)、(4)两种排序方式最终得到的分数是相同的,并且在线下数据集中,根据这两种方式排序,得到的服务器的顺序大致是相同的。(很可能是我们猜测的系数刚好符合简化后数据集的系数)。
添加虚拟机
如果在当前服务器有满足需要部署的虚拟机所需要资源的情况下,我们部署的策略是,首先遍历所有服务器,找到当前虚拟机部署在第一次出现的合适的服务器,对于双节点虚拟机,计算其部署后剩余资源的大小,即剩余CPU * 0.75 + 剩余内存 * 0.25,如果为单节点虚拟机,
只计算其一个节点的剩余资源,计算公式还是如上所示。在单节点中,选取A、B节点也要考虑两个节点之间的负载均衡,因为后面可能会出现双节点虚拟机不会因为负载不均衡而无法部署。最终找到剩余资源最小的那个服务器进行部署,具体代码如下所示:
for (int i = 0; i < sizes; i++)
{
if (vv.Is_Double_node)
{
if (server_myselfs[i].A_cpu >= vv.cpu / 2 && server_myselfs[i].A_Memory >= vv.Memory / 2 && server_myselfs[i].B_cpu >= vv.cpu / 2 && server_myselfs[i].B_Memory >= vv.Memory / 2)
{
if (((server_myselfs[i].A_cpu + server_myselfs[i].B_cpu - vv.cpu) * Q3 + (server_myselfs[i].A_Memory + server_myselfs[i].B_Memory - vv.Memory) * Q4) < myself_max)
{
myself_max = (server_myselfs[i].A_cpu + server_myselfs[i].B_cpu - vv.cpu) * Q3 + (server_myselfs[i].A_Memory + server_myselfs[i].B_Memory - vv.Memory) * Q4;
myself_idnex = i;
flag = 1;
}
}
}
else
{
int flag1 = 0, flag2 = 0;
if (server_myselfs[i].A_cpu >= vv.cpu && server_myselfs[i].A_Memory >= vv.Memory)
{
if (((server_myselfs[i].A_cpu - vv.cpu) * Q3 + (server_myselfs[i].A_Memory - vv.Memory) * Q4) < myself_max)
{
myself_max = (server_myselfs[i].A_cpu - vv.cpu) * Q3 + (server_myselfs[i].A_Memory - vv.Memory) * Q4;
myself_idnex = i;
A_or_B = 1;
flag = 1;
flag1 = 1;
}
}
int myself_max_tmp = myself_max;
if (server_myselfs[i].B_cpu >= vv.cpu && server_myselfs[i].B_Memory >= vv.Memory)
{
if (((server_myselfs[i].B_cpu - vv.cpu) * Q3 + (server_myselfs[i].B_Memory - vv.Memory) * Q4) < myself_max)
{
myself_max = (server_myselfs[i].B_cpu - vv.cpu) * Q3 + (server_myselfs[i].B_Memory - vv.Memory) * Q4;
myself_idnex = i;
A_or_B = 0;
flag = 1;
flag2 = 1;
}
}
if (myself_max_tmp <= myself_max && flag1 == 1 && flag2 == 1)
{
myself_max = myself_max_tmp;
A_or_B = 1;
flag = 1;
}
if (myself_max_tmp >= myself_max && flag1 == 1 && flag2 == 1)
{
A_or_B = 0;
flag = 1;
}
}
}
迁移调度
在迁移过程中,我们使用的是一层虚拟机循环,一层服务器循环,我们对上一天中的所有虚拟机进行遍历,在循环中将这个虚拟机部署到其他合适的服务器上,首先去计算当前虚拟机在当前服务器CPU和内存的使用率,**当前CPU和内存的剩余资源所占总资源的百分比小于0.07,那么我们直接跳过,**这样做的理由如下:
- 首先可以加速,在正式赛中,数据量比线下的数据量要大很多,我们可以在牺牲一部分迁移的情况下来避免超时,正式赛的超时现象很严重。
- 其次,在服务器所剩余资源较小的情况下,我们可以认为当前服务器已经达到了负载均衡,满足理想中的条件,如果再加迁移,可能会影响均衡。
在正式赛的提交中,我们的代码在取该值为0.07时,得到的分数是最好的。由于迁移代码较长,就不在此处进行放置,大家可以在我们公布的源码中进行理解。
可以优化的思路
因为我们的当前版本的分数可以进入复赛,加上队友后期有事,就没有花太多精力去进行其他方面的优化(也是本人比较菜,写了几个bug,懒得改了)现在分享一些其我们想到的他方面的一些没有实现的优化吧:
- 可以将每天的请求进行提前保存,对需要部署的虚拟机进行排序选择,或者可以根据当天的虚拟机的CPU和内存进行拟合,对服务器重新排序,选择合适的服务器。
- 可以在每次迁移前,对所有的服务器进行排序,将利用率较小的服务器迁移到利用率较高的服务器上去。
- 可以在整个添加服务器上进行单双节点分治,即单节点部署的虚拟机可以迁移到有双节点部署的服务器上去,但是双节点部署的虚拟机不可以迁移到只有单节点部署的服务器上面,这点在我们中间版本的代码中是有一定提升的,但是因为后面的版本有了一些改动,所以就没有继续采用,大佬们可以自己实验以下。
- 服务器的选择,线上和线下的服务器中了以及特征是差不多的,因为上面有两种排序得到结果是相同的,线下的数据也是相同的。我们的服务器选择不算差,但是与前排大佬比起来还是逊色一些的,所以服务器的选择可以使用一些更高级的拟合来进行。
队伍正式赛分数最优代码
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <fstream>
#include <algorithm>
#include <ctime>
using namespace std;
//clock_t startTime, endTime;
// 服务器
typedef struct A
{
//型号, CPU核数, 内存大小, 硬件成本, 能耗成本
string model; //型号
double cpu; //CPU核数
double Memory; //内存大小
long long Hardware_cost; //硬件成本
long long Energy_cost_day; //每日能耗成本
} server;
// 虚拟机
typedef struct B
{
//型号, CPU核数, 内存大小, 是否双节点部署
string model; //型号
double cpu; //CPU核数
double Memory; //内存大小
int Is_Double_node; //是否双节点部署
} VM;
//用来存放当前所有服务器的信息的
typedef struct C
{
string model; //型号
double A_cpu; //已经当前还未使用数量
double A_Memory;
double B_cpu;
double B_Memory;
long long Energy_cost_day;
int flag;
vector<pair<int, int>> VM_ids;
} server_myself;
// 每一天中增加的虚拟机信息
typedef struct D
{
string model;
int index;
int server_id; //存放在哪一个server中
int Is_Double_node; //是否使用双节点部署
int A_or_B; //如果使用的是单节点部署,那么部署在哪个节点,1代表A
} add_VM;
typedef struct E
{
string op;
string model;
int id;
} operators;
int cost; //这个用来记录消费,用来评估算法水平
unordered_map<string, server> server_map; //存储所有服务器的信息
server server_buf;
vector<pair<server, double>> model_pair;
unordered_map<string, VM> VM_map;
VM VM_buf;
vector<pair<int, int>> ids_pair;
unordered_map<int, add_VM> adds;
vector<server_myself> server_myselfs;
int day = 0;
int migrations_index = 0;
int sum_vm = 0;
vector<int> cin_size;
vector<operators> cin_buf;
string buf;
double Q1 = 0.75, Q2 = 0.25;
double Q3 = 0.75, Q4 = 0.25;
void InitServer(string buf)
{
int j = 1;
server_buf.model.clear();
// 服务器型号
while (buf[j] != ',')
{
server_buf.model.push_back(buf[j]);
j++;
}
j++;
while (buf[j] == ' ')
{
j++;
}
// 服务CPU大小
server_buf.cpu = 0;
while (buf[j] != ',')
{
server_buf.cpu = server_buf.cpu * 10 + buf[j] - '0';
j++;
}
j++;
while (buf[j] == ' ')
{
j++;
}
// 服务内存大小
server_buf.Memory = 0;
while (buf[j] != ',')
{
server_buf.Memory = server_buf.Memory * 10 + buf[j] - '0';
j++;
}
j++;
while (buf[j] == ' ')
{
j++;
}
// 服务硬件成本
server_buf.Hardware_cost = 0;
while (buf[j] != ',')
{
server_buf.Hardware_cost = server_buf.Hardware_cost * 10 + buf[j] - '0';
j++;
}
j++;
while (buf[j] == ' ')
{
j++;
}
// 服务耗能成本
server_buf.Energy_cost_day = 0;
while (buf[j] != ')')
{
server_buf.Energy_cost_day = server_buf.Energy_cost_day * 10 + buf[j] - '0';
j++;
}
server_map[server_buf.model] = server_buf;
//double weight = server_buf.cpu * 0.75 + server_buf.Memory * 0.25;
double weight = server_buf.Energy_cost_day;
model_pair.push_back(make_pair(server_buf, weight));
}
void InitVM(string buf)
{
int j = 1;
VM_buf.model.clear();
while (buf[j] != ',')
{
VM_buf.model.push_back(buf[j]);
j++;
}
j++;
while (buf[j] == ' ')
{
j++;
}
VM_buf.cpu = 0;
while (buf[j] != ',')
{
VM_buf.cpu = VM_buf.cpu * 10 + buf[j] - '0';
j++;
}
j++;
while (buf[j] == ' ')
{
j++;
}
VM_buf.Memory = 0;
while (buf[j] != ',')
{
VM_buf.Memory = VM_buf.Memory * 10 + buf[j] - '0';
j++;
}
j++;
while (buf[j] == ' ')
{
j++;
}
VM_buf.Is_Double_node = buf[j] - '0';
VM_map[VM_buf.model] = VM_buf;
}
string Operation(string buf, int &index)
{
string op;
while (buf[index] != ',')
{
op.push_back(buf[index]);
index++;
}
index++;
while (buf[index] == ' ')
{
index++;
}
return op;
}
bool static cmp(pair<server, double> &A, pair<server, double> &B)
{
return A.second < B.second;
}
bool static cmp2(server_myself &A, server_myself &B)
{
return A.A_cpu + A.B_cpu < B.A_cpu + B.B_cpu;
}
/*
bool static cmp3(server_myself &A, server_myself &B)
{
return A.id < B.id;
}
*/
//ofstream outfile("out2.txt", ios::trunc);
//ofstream outfile2("out.txt", ios::trunc);
void Select(int &sum, vector<string> &migrations)
{
int j = migrations_index;
int server_myselfs_len = (int)server_myselfs.size();
int ids_pair_len = (int)ids_pair.size();
int find_sum = 0;
// sort(server_myselfs.begin(), server_myselfs.end(), cmp2);
while (1)
{
j++;
find_sum++;
j = j % ids_pair_len;
if (j == migrations_index)
{
break;
}
// if(find_sum>ids_pair_len/2){
// break;
// }
//调度不能超过总数的5/1000;
if (sum >= (sum_vm / 1000 * 5 - 1))
{
break;
}
if (ids_pair[j].second)
{
//表示当前虚拟机还存在的
add_VM temp = adds[ids_pair[j].first];
int max_index = temp.server_id;
int max_index_or = max_index;
/*
if (max_index != server_myselfs[max_index].id)
{
cout << "Error" << endl;
}
*/
double max, max1, max2;
if (temp.Is_Double_node)
{
max = (server_myselfs[max_index].A_cpu + server_myselfs[max_index].B_cpu) * Q1 + (server_myselfs[max_index].A_Memory + server_myselfs[max_index].B_Memory) * Q2;
max1 = (server_myselfs[max_index].A_cpu + server_myselfs[max_index].B_cpu) * 1.0 / (server_map[server_myselfs[max_index].model].cpu);
max2 = (server_myselfs[max_index].A_Memory + server_myselfs[max_index].B_Memory) * 1.0 / (server_map[server_myselfs[max_index].model].Memory);
}
else
{
if (temp.A_or_B)
{
max = server_myselfs[max_index].A_cpu * Q1 + server_myselfs[max_index].A_Memory * Q2;
max1 = server_myselfs[max_index].A_cpu * 1.0 / (server_map[server_myselfs[max_index].model].cpu / 2);
max2 = server_myselfs[max_index].A_Memory * 1.0 / (server_map[server_myselfs[max_index].model].Memory / 2);
}
else
{
max = server_myselfs[max_index].B_cpu * Q1 + server_myselfs[max_index].B_Memory * Q2;
max1 = server_myselfs[max_index].B_cpu * 1.0 / (server_map[server_myselfs[max_index].model].cpu / 2);
max2 = server_myselfs[max_index].B_Memory * 1.0 / (server_map[server_myselfs[max_index].model].Memory / 2);
}
}
if (max1 < 0.07 && max2 < 0.07)
{
continue;
}
int flag = 0;
int A_or_B = 0;
VM vv = VM_map[temp.model];
for (int i = 0; i < server_myselfs_len; i++)
{
if (i == max_index_or)
{
continue;
}
if (vv.Is_Double_node)
{
if (server_myselfs[i].A_cpu >= vv.cpu / 2 && server_myselfs[i].A_Memory >= vv.Memory / 2 && server_myselfs[i].B_cpu >= vv.cpu / 2 && server_myselfs[i].B_Memory >= vv.Memory / 2)
{
if (((server_myselfs[i].A_cpu + server_myselfs[i].B_cpu - vv.cpu) * Q1 + (server_myselfs[i].A_Memory + server_myselfs[i].B_Memory - vv.Memory) * Q2) < max)
{
max = (server_myselfs[i].A_cpu + server_myselfs[i].B_cpu - vv.cpu) * Q1 + (server_myselfs[i].A_Memory + server_myselfs[i].B_Memory - vv.Memory) * Q2;
max_index = i;
flag = 1;
}
}
}
else
{
if (server_myselfs[i].A_cpu >= vv.cpu && server_myselfs[i].A_Memory >= vv.Memory)
{
if (((server_myselfs[i].A_cpu - vv.cpu) * Q1 + (server_myselfs[i].A_Memory - vv.Memory) * Q2) < max)
{
max = (server_myselfs[i].A_cpu - vv.cpu) * Q1 + (server_myselfs[i].A_Memory - vv.Memory) * Q2;
max_index = i;
A_or_B = 1;
flag = 1;
}
}
if (server_myselfs[i].B_cpu >= vv.cpu && server_myselfs[i].B_Memory >= vv.Memory)
{
if (((server_myselfs[i].B_cpu - vv.cpu) * Q1 + (server_myselfs[i].B_Memory - vv.Memory) * Q2) < max)
{
max = (server_myselfs[i].B_cpu - vv.cpu) * Q1 + (server_myselfs[i].B_Memory - vv.Memory) * Q2;
max_index = i;
A_or_B = 0;
flag = 1;
}
}
}
}
if (flag)
{
//outfile2 << "day: " << day << "j: " << j << " ," << ids_pair_len << endl;
add_VM tt = adds[ids_pair[j].first];
sum++;
if (vv.Is_Double_node)
{
//还原
server_myselfs[max_index_or].A_cpu += vv.cpu / 2;
server_myselfs[max_index_or].A_Memory += vv.Memory / 2;
server_myselfs[max_index_or].B_cpu += vv.cpu / 2;
server_myselfs[max_index_or].B_Memory += vv.Memory / 2;
server_myselfs[max_index].A_cpu -= vv.cpu / 2;
server_myselfs[max_index].A_Memory -= vv.Memory / 2;
server_myselfs[max_index].B_cpu -= vv.cpu / 2;
server_myselfs[max_index].B_Memory -= vv.Memory / 2;
tt.server_id = max_index;
tt.Is_Double_node = 1;
migrations.push_back("(" + to_string(ids_pair[j].first) + "," + to_string(max_index) + ")");
}
else
{
if (tt.A_or_B)
{
server_myselfs[max_index_or].A_cpu += vv.cpu;
server_myselfs[max_index_or].A_Memory += vv.Memory;
}
else
{
server_myselfs[max_index_or].B_cpu += vv.cpu;
server_myselfs[max_index_or].B_Memory += vv.Memory;
}
if (A_or_B)
{
tt.A_or_B = 1;
tt.server_id = max_index;
server_myselfs[max_index].A_cpu -= vv.cpu;
server_myselfs[max_index].A_Memory -= vv.Memory;
migrations.push_back("(" + to_string(ids_pair[j].first) + "," + to_string(max_index) + ",A" + ")");
}
else
{
tt.A_or_B = 0;
tt.server_id = max_index;
server_myselfs[max_index].B_cpu -= vv.cpu;
server_myselfs[max_index].B_Memory -= vv.Memory;
migrations.push_back("(" + to_string(ids_pair[j].first) + "," + to_string(max_index) + ",B" + ")");
}
}
if ((server_myselfs[max_index_or].A_cpu + server_myselfs[max_index_or].B_cpu) == server_map[server_myselfs[max_index_or].model].cpu)
{
if ((server_myselfs[max_index_or].A_Memory + server_myselfs[max_index_or].B_Memory) == server_map[server_myselfs[max_index_or].model].Memory)
{
server_myselfs[max_index_or].flag = 0;
}
}
adds[ids_pair[j].first] = tt;
}
}
}
migrations_index = j;
}
void ReadallData(int T)
{
for (int k = 0; k < T; k++)
{
int R;
cin >> R;
getchar();
cin_size.push_back(R);
for (int j = 0; j < R; j++)
{
getline(cin, buf);
int index = 1;
operators ops;
ops.op = Operation(buf, index);
if (ops.op == "add")
{
while (buf[index] != ',')
{
ops.model.push_back(buf[index]);
index++;
}
index++;
while (buf[index] == ' ')
{
index++;
}
ops.id = 0; // 虚拟机ID
while (buf[index] != ')')
{
ops.id = ops.id * 10 + buf[index] - '0';
index++;
}
}
else
{
ops.id = 0;
while (buf[index] != ')')
{
ops.id = ops.id * 10 + buf[index] - '0';
index++;
}
}
cin_buf.push_back(ops);
}
}
}
int main()
{
//startTime = clock();
int N; //服务器种类
cin >> N;
getchar();
for (int i = 0; i < N; i++)
{
getline(cin, buf);
InitServer(buf);
}
server server_tem;
int max = 10000000, server_tem_index = 0;
server_tem_index = 11;
sort(model_pair.begin(), model_pair.end(), cmp);
//M
int M;
cin >> M;
getchar();
for (int i = 0; i < M; i++)
{
getline(cin, buf);
InitVM(buf);
}
//T
int T;
cin >> T;
ReadallData(T);
int model_pair_size = model_pair.size();
long long cost_min = 5000000000;
long long cost = 0;
vector<string> res_min;
int i1_index = 0;
cost = 0;
vector<string> res;
unordered_map<string, int> purchase; //用来存放服务器当天使用种类
vector<pair<string, int>> purchase_num; //用来存放服务器当天使用种类个数
for (int k = 0; k < T; k++)
{
day++;
vector<string> migrations;
int sum = 0;
if (day % 1 == 0 && day != 1)
{
Select(sum, migrations);
}
purchase.clear();
purchase_num.clear();
purchase_num.push_back(make_pair("000", 0)); //purchase_num第0个是没有用的
vector<string> dis;
int len = server_myselfs.size();
vector<vector<int>> ids;
vector<int> dis_id;
vector<operators> day_ops;
for (int j = 0; j < cin_size[k]; j++)
{
operators ops = cin_buf[i1_index++];
if (ops.op == "add")
{
sum_vm++;
int flag = 0;
VM vv = VM_map[ops.model];
add_VM add_tt;
add_tt.model = ops.model;
add_tt.Is_Double_node = 0;
dis_id.push_back(ops.id);
int myself_idnex = 0, A_or_B = 0;
double myself_max = 10000000;
int sizes = server_myselfs.size();
for (int i = 0; i < sizes; i++)
{
if (vv.Is_Double_node)
{
if (server_myselfs[i].A_cpu >= vv.cpu / 2 && server_myselfs[i].A_Memory >= vv.Memory / 2 && server_myselfs[i].B_cpu >= vv.cpu / 2 && server_myselfs[i].B_Memory >= vv.Memory / 2)
{
if (((server_myselfs[i].A_cpu + server_myselfs[i].B_cpu - vv.cpu) * Q3 + (server_myselfs[i].A_Memory + server_myselfs[i].B_Memory - vv.Memory) * Q4) < myself_max)
{
myself_max = (server_myselfs[i].A_cpu + server_myselfs[i].B_cpu - vv.cpu) * Q3 + (server_myselfs[i].A_Memory + server_myselfs[i].B_Memory - vv.Memory) * Q4;
myself_idnex = i;
flag = 1;
}
}
}
else
{
int flag1 = 0, flag2 = 0;
if (server_myselfs[i].A_cpu >= vv.cpu && server_myselfs[i].A_Memory >= vv.Memory)
{
if (((server_myselfs[i].A_cpu - vv.cpu) * Q3 + (server_myselfs[i].A_Memory - vv.Memory) * Q4) < myself_max)
{
myself_max = (server_myselfs[i].A_cpu - vv.cpu) * Q3 + (server_myselfs[i].A_Memory - vv.Memory) * Q4;
myself_idnex = i;
A_or_B = 1;
flag = 1;
flag1 = 1;
}
}
int myself_max_tmp = myself_max;
if (server_myselfs[i].B_cpu >= vv.cpu && server_myselfs[i].B_Memory >= vv.Memory)
{
if (((server_myselfs[i].B_cpu - vv.cpu) * Q3 + (server_myselfs[i].B_Memory - vv.Memory) * Q4) < myself_max)
{
myself_max = (server_myselfs[i].B_cpu - vv.cpu) * Q3 + (server_myselfs[i].B_Memory - vv.Memory) * Q4;
myself_idnex = i;
A_or_B = 0;
flag = 1;
flag2 = 1;
}
}
if (myself_max_tmp <= myself_max && flag1 == 1 && flag2 == 1)
{
myself_max = myself_max_tmp;
A_or_B = 1;
flag = 1;
}
if (myself_max_tmp >= myself_max && flag1 == 1 && flag2 == 1)
{
A_or_B = 0;
flag = 1;
}
}
}
if (flag)
{
server_myselfs[myself_idnex].VM_ids.push_back(make_pair(ops.id, 1));
server_myselfs[myself_idnex].flag = 1;
if (vv.Is_Double_node)
{
add_tt.Is_Double_node = 1;
server_myselfs[myself_idnex].A_cpu -= vv.cpu / 2;
server_myselfs[myself_idnex].A_Memory -= vv.Memory / 2;
server_myselfs[myself_idnex].B_cpu -= vv.cpu / 2;
server_myselfs[myself_idnex].B_Memory -= vv.Memory / 2;
add_tt.server_id = myself_idnex;
if (myself_idnex >= len)
{
ids[myself_idnex - len].push_back(ops.id);
}
}
else
{
if (A_or_B)
{
add_tt.A_or_B = 1;
server_myselfs[myself_idnex].A_cpu -= vv.cpu;
server_myselfs[myself_idnex].A_Memory -= vv.Memory;
add_tt.server_id = myself_idnex;
if (myself_idnex >= len)
{
ids[myself_idnex - len].push_back(ops.id);
}
}
else
{
add_tt.A_or_B = 0;
server_myselfs[myself_idnex].B_cpu -= vv.cpu;
server_myselfs[myself_idnex].B_Memory -= vv.Memory;
add_tt.server_id = myself_idnex;
if (myself_idnex >= len)
{
ids[myself_idnex - len].push_back(ops.id);
}
}
}
}
else
{
int ii;
for (ii = 0; ii < model_pair_size; ii++)
{
//找到一个最合适服务器的
if (vv.cpu < model_pair[ii].first.cpu / 2 && vv.Memory < model_pair[ii].first.Memory / 2 && !vv.Is_Double_node)
{
break;
}
if (vv.cpu < model_pair[ii].first.cpu && vv.Memory < model_pair[ii].first.Memory && vv.Is_Double_node)
{
break;
}
}
// if((vv.cpu<model_pair[server_tem_index].first.cpu/2&&vv.Memory<model_pair[server_tem_index].first.Memory/2&&!vv.Is_Double_node)||(vv.cpu<model_pair[server_tem_index].first.cpu&&vv.Memory<model_pair[server_tem_index].first.Memory&&vv.Is_Double_node)){
// ii = server_tem_index;
// }
// else{
/*
double tmp1 = vv.cpu * 1.0 / vv.Memory * 1000;
double maxn = 999999;
for (int ii = 0; ii < model_pair_size; ii++)
{ //找到一个最合适服务器的
double tmp2 = model_pair[ii].first.cpu * 1.0 / model_pair[ii].first.Memory * 1000;
if (vv.cpu < model_pair[ii].first.cpu / 2 && vv.Memory < model_pair[ii].first.Memory / 2 && !vv.Is_Double_node)
{
if (abs(tmp1 - tmp2) < maxn)
{
iii = ii;
maxn = abs(tmp1 - tmp2);
}
}
if (vv.cpu < model_pair[ii].first.cpu && vv.Memory < model_pair[ii].first.Memory && vv.Is_Double_node)
{
if (abs(tmp1 - tmp2) < maxn)
{
iii = ii;
maxn = abs(tmp1 - tmp2);
}
}
}
*/
// }
server ss = model_pair[ii].first;
cost += ss.Hardware_cost;
server_myself tt;
tt.A_cpu = ss.cpu / 2;
tt.A_Memory = ss.Memory / 2;
tt.B_cpu = ss.cpu / 2;
tt.B_Memory = ss.Memory / 2;
tt.model = ss.model;
tt.Energy_cost_day = ss.Energy_cost_day;
tt.flag = 1;
int tt_id = 0;
int tt_index = 0;
if (purchase[ss.model] == 0)
{
purchase[ss.model] = purchase_num.size();
purchase_num.push_back(make_pair(ss.model, 1));
tt_id = server_myselfs.size();
tt_index = ids.size();
server_myselfs.push_back(tt);
ids.push_back({
ops.id});
}
else
{
if (purchase[ss.model] == (int)purchase_num.size())
{
tt_id = server_myselfs.size();
tt_index = ids.size();
server_myselfs.push_back(tt);
ids.push_back({
ops.id});
}
else
{
int sum = 0;
for (int i = 1; i <= purchase[ss.model]; i++)
{
sum += purchase_num[i].second;
}
tt_id = len + sum;
tt_index = sum;
server_myselfs.insert(server_myselfs.begin() + tt_id, tt);
vector<int> temp = {
ops.id};
ids.insert(ids.begin() + sum, temp);
for (int i = sum + 1; i < (int)ids.size(); i++)
{
for (int j = 0; j < (int)ids[i].size(); j++)
{
if (adds.find(ids[i][j]) != adds.end())
{
adds[ids[i][j]].server_id++;
}
}
}
}
purchase_num[purchase[ss.model]].second++;
}
server_myselfs[tt_id].flag = 1;
server_myselfs[tt_id].VM_ids.push_back(make_pair(ops.id, 1));
if (vv.Is_Double_node)
{
add_tt.Is_Double_node = 1;
if (vv.cpu / 2 <= server_myselfs[tt_id].A_cpu && vv.Memory / 2 <= server_myselfs[tt_id].A_Memory)
{
server_myselfs[tt_id].A_cpu -= vv.cpu / 2;
server_myselfs[tt_id].A_Memory -= vv.Memory / 2;
server_myselfs[tt_id].B_cpu -= vv.cpu / 2;
server_myselfs[tt_id].B_Memory -= vv.Memory / 2;
add_tt.server_id = tt_id;
}
else
{
cout << "----------CPU OR Memory 有问题的" << endl;
return 0;
}
}
else
{
add_tt.A_or_B = 1;
if (vv.cpu <= server_myselfs[tt_id].A_cpu && vv.Memory <= server_myselfs[tt_id].A_Memory)
{
server_myselfs[tt_id].A_cpu -= vv.cpu;
server_myselfs[tt_id].A_Memory -= vv.Memory;
add_tt.server_id = tt_id;
}
else
{
cout << "----------CPU OR Memory 有问题的" << endl;
return 0;
}
}
}
add_tt.index = (int)ids_pair.size();
ids_pair.push_back(make_pair(ops.id, 1));
adds[ops.id] = add_tt;
}
else
{
//回收部分
add_VM del = adds[ops.id];
ids_pair[del.index].second = 0;
sum_vm--;
if (del.Is_Double_node)
{
server_myselfs[del.server_id].A_cpu += VM_map[del.model].cpu / 2;
server_myselfs[del.server_id].A_Memory += VM_map[del.model].Memory / 2;
server_myselfs[del.server_id].B_cpu += VM_map[del.model].cpu / 2;
server_myselfs[del.server_id].B_Memory += VM_map[del.model].Memory / 2;
}
else
{
if (del.A_or_B)
{
server_myselfs[del.server_id].A_cpu += VM_map[del.model].cpu;
server_myselfs[del.server_id].A_Memory += VM_map[del.model].Memory;
}
else
{
server_myselfs[del.server_id].B_cpu += VM_map[del.model].cpu;
server_myselfs[del.server_id].B_Memory += VM_map[del.model].Memory;
}
}
for (int i = 0; i < (int)server_myselfs[del.server_id].VM_ids.size(); i++)
{
if (server_myselfs[del.server_id].VM_ids[i].first == ops.id)
{
server_myselfs[del.server_id].VM_ids[i].second = 0;
}
}
if ((server_myselfs[del.server_id].A_cpu + server_myselfs[del.server_id].B_cpu) == server_map[server_myselfs[del.server_id].model].cpu)
{
if ((server_myselfs[del.server_id].A_Memory + server_myselfs[del.server_id].B_Memory) == server_map[server_myselfs[del.server_id].model].Memory)
{
server_myselfs[del.server_id].flag = 0;
}
}
//adds.erase(id);
}
}
/*
for (int i = 0; i < (int)server_myselfs.size(); i++)
{
if (server_myselfs[i].flag)
{
cost += server_myselfs[i].Energy_cost_day;
}
}
*/
for (int i = 0; i < (int)dis_id.size(); i++)
{
if (adds[dis_id[i]].Is_Double_node)
{
dis.push_back("(" + to_string(adds[dis_id[i]].server_id) + ")");
}
else
{
if (adds[dis_id[i]].A_or_B)
{
dis.push_back("(" + to_string(adds[dis_id[i]].server_id) + "," + "A)");
}
else
{
dis.push_back("(" + to_string(adds[dis_id[i]].server_id) + "," + "B)");
}
}
}
res.push_back("(purchase," + to_string(purchase_num.size() - 1) + ")");
for (int i = 1; i < (int)purchase_num.size(); i++)
{
res.push_back("(" + purchase_num[i].first + "," + to_string(purchase_num[i].second) + ")");
}
res.push_back("(migration," + to_string(migrations.size()) + ")"); //一个简单的调度算法的
for (int i = 0; i < (int)migrations.size(); i++)
{
res.push_back(migrations[i]);
}
for (int i = 0; i < (int)dis.size(); i++)
{
res.push_back(dis[i]);
}
}
for (int i = 0; i < (int)res.size(); i++)
{
cout << res[i] << endl;
//outfile << res[i] << endl;
}
// cout << cost_min << endl;
//cout << cost << endl;
//endTime = clock();
// cout << "The run time is:" <<(double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
return 0;
}
复赛的题目应该是在初赛上加一些约束或者其他的数据,这里面的优化思路还是有一定的指引性的,仅供参考。如果在我的博客中有什么写的不妥的,或者错误的地方,欢迎大家留言批评指正。
凡不能摧毁我者,必将使我更强大!!!