最终提交代码
import java.util.*;
/**
* @author SJ
* @date 2020/12/19
*/
public class NewSolution {
/**
* --------------------------------基石----------------------------------------
*/
public static Scanner scanner = new Scanner(System.in);
public static List<Server> serverList = new ArrayList<>();//存储所有的服务器信息
public static List<VMs> vms = new ArrayList<>();//存储所有的虚拟机信息
public static int step = 1;//总共需要几步
public static List<List<List<Integer>>> ans = new ArrayList<>();//存入具体的步骤
//虚拟机类
public static class VMs {
public int ID;
public int needCores;//占用核心数
public int needRAM;//占用内存
public int location;//当前位置
public int destination;//需要移动到的位置
public int STATE = 0;//=0 表示在正确的位置 =1表示等待目标服务器分配资源转出
public VMs(int ID, int needCores, int needRAM) {
this.ID = ID;
this.needCores = needCores;
this.needRAM = needRAM;
}
//输入虚拟机信息
public static void input(int N) {
for (int i = 0; i < N; i++) {
// System.out.println("输入核心数和内存");
//虚拟机要存的地方:大的虚拟机列表,对应服务器优先队列,目标服务器的期待列表,当前服务器的转出列表
int nC = scanner.nextInt();
int nM = scanner.nextInt();
VMs vm = new VMs(i, nC, nM);
vms.add(vm);
}
for (int i = 0; i < N; i++) {
// System.out.println("输入移动");
int curLocation = scanner.nextInt();
int des = scanner.nextInt();
VMs tempVM = vms.get(i);
tempVM.location = curLocation;
tempVM.destination = des;
Server myServer = serverList.get(curLocation);
if (curLocation != des) {
tempVM.STATE = 1;
serverList.get(des).wantVM.add(tempVM);
myServer.isNotMyVM.add(tempVM);
} else
completeIndex.add(i);
//更新服务器
myServer.coresLeft -= tempVM.needCores;
myServer.ActuCoreLeft -= tempVM.needCores;
myServer.RAMLeft -= tempVM.needRAM;
myServer.ActuRAMLeft -= tempVM.needRAM;
}
}
}
//服务器类
public static class Server {
public int ID;//编号
public int totalCores;//总核心数
public int totalRAM;//总内存数
public int coresLeft;//剩余核心数;
public int RAMLeft;//剩余内存数
public int process = 0;
public int ActuCoreLeft;
public int ActuRAMLeft;
public List<VMs> wantVM = new LinkedList<>();//期待的虚拟机
public List<VMs> isNotMyVM = new LinkedList<>();//暂存在这里的虚拟机需要移除的虚拟机
public Server(int ID, int totalCores, int totalRAM, int coresLeft, int RAMLeft, int actuCoreLeft, int actuRAMLeft) {
this.ID = ID;
this.totalCores = totalCores;
this.totalRAM = totalRAM;
this.coresLeft = coresLeft;
this.RAMLeft = RAMLeft;
ActuCoreLeft = actuCoreLeft;
ActuRAMLeft = actuRAMLeft;
}
//输入数据
public static void input(int N) {
for (int i = 0; i < N; i++) {
int tC = scanner.nextInt();
int tR = scanner.nextInt();
serverList.add(new Server(i, tC, tR, tC, tR, tC, tR));
}
}
}
/**
* --------------------------------常用工具--------------------------------
*/
//存入已到位的虚拟机编号
public static Set<Integer> completeIndex = new HashSet<>();
//看看这一step满没满
public Boolean check() {
if (ans.size() == step && ans.get(step - 1).size() == serverList.size())
return true;
else
return false;
}
//是否所有任务都已完成
public Boolean isMissionCompleted() {
return completeIndex.size() == vms.size();
}
//服务器排序
public int demand(Server server) {
int sum = 0;
List<VMs> wantVM = server.wantVM;
for (VMs vMs : wantVM) {
sum += vMs.needRAM;
}
return sum - server.RAMLeft;
}
Comparator<Server> serverComparator = new Comparator<Server>() {
@Override
public int compare(Server o1, Server o2) {
return demand(o2) - demand(o1);
}
};
//虚拟机排序
Comparator<VMs> vMsComparator = new Comparator<VMs>() {
@Override
public int compare(VMs o1, VMs o2) {
return o2.needRAM - o1.needRAM;
}
};
/**
* --------------------------核心方法--------------------------------
*/
//移动虚拟机
Boolean changeFlag = false;
public Boolean move(Server serverOne, Server serverTwo, VMs vm) {
//从一台虚拟机移动到另一台虚拟机需要进行的操作
//1.判断当前虚拟机进程数,=2则返回
//2.目标虚拟机内存和核心数够不够 不够则返回
//如果可以移动
//1.两个服务器进程数量+1
//2.更新服务器的内存和核心数
//3.修改对应服务器的期待和出列表
//4.在one的优先队列出队
//5.在two的优先队列入队
//6.更新虚拟机的所在地,更新虚拟机状态。
if (serverOne.process == 2 || serverTwo.process == 2
|| serverTwo.ActuRAMLeft < vm.needRAM
|| serverTwo.RAMLeft < vm.needRAM
|| serverTwo.ActuCoreLeft < vm.needCores
|| serverTwo.coresLeft < vm.needCores
|| vm.destination == vm.location//已经在家了,不许跑
|| serverOne.ID == serverTwo.ID //自己移动自己
|| (ans.size() == step && ans.get(step - 1).size() == serverList.size())//已经N步
)
return false;
changeFlag = true;
serverOne.process++;
serverTwo.process++;
serverChanged.add(serverOne.ID);
serverChanged.add(serverTwo.ID);
serverOne.coresLeft += vm.needCores;
serverOne.RAMLeft += vm.needRAM;
serverTwo.coresLeft -= vm.needCores;
serverTwo.RAMLeft -= vm.needRAM;
vm.location = serverTwo.ID;
/* serverTwo.myVM.add(vm);
serverOne.myVM.remove(vm);*/
//到了目标位置
if (serverTwo.ID == vm.destination) {
completeIndex.add(vm.ID);
// vm.STATE = 0;
serverOne.isNotMyVM.remove(vm);
// System.out.println(remove);
serverTwo.wantVM.remove(vm);
// System.out.println(remove1);
} else {//移到别人家
serverOne.isNotMyVM.remove(vm);
serverTwo.isNotMyVM.add(vm);
}
//记录这一步
if (ans.size() < step) {
List<List<Integer>> oneStepAns;
oneStepAns = new ArrayList<>();
oneStepAns.add(Arrays.asList(serverOne.ID, serverTwo.ID, vm.ID));
ans.add(oneStepAns);
} else {
ans.get(step - 1).add(Arrays.asList(serverOne.ID, serverTwo.ID, vm.ID));
}
return true;
}
//释放资源
public static Set<Integer> serverChanged = new HashSet<>();
public void releaseResource() {
for (Integer index : serverChanged) {
Server server = serverList.get(index);
server.process = 0;
server.ActuCoreLeft = server.coresLeft;
server.ActuRAMLeft = server.RAMLeft;
//更新虚拟机状态
// updateServerState(server);
changeFlag = false;
}
serverChanged.clear();
step++;
}
/**
* ---------------------------------解锁-------------------------------
*/
public static Set<Integer> vmChanged = new HashSet<>();//移动过的虚拟机
public static Set<Integer> serverOut = new HashSet<>();//负责出的服务器
public static Set<Integer> serverIn = new HashSet<>();//负责进的服务器
public void openLock2() {
int unCom = vms.size() - completeIndex.size();//未到位的虚拟机个数
int N = serverList.size();
int times = Math.min(unCom / 2 + 1, N / 2);
// int times = N/2;
for (int i = 0; i < serverList.size() && times != 0; i++) {
Server server = serverList.get(i);
if (serverIn.contains(server.ID))//用来进的虚拟机不允许再出
continue;
for (int i1 = 0; i1 < server.isNotMyVM.size(); i1++) {
VMs vMs = server.isNotMyVM.get(i1);
if (vmChanged.contains(vMs.ID))//移动过的虚拟机不允许再动
{
continue;
} else {
Boolean serverIn = false;
serverIn = findServerIn(server, vMs);
if (serverIn) {
serverOut.add(server.ID);//记录腾出空间的虚拟机
times--;//完成一次移动
}
}
}
}
}
public void startAfterOpenLock() {
for (Integer integer : serverOut) {
Server server = serverList.get(integer);
List<VMs> wantVM = server.wantVM;
if (wantVM.size() == 0)
continue;
else {
wantVM.sort(vMsComparator);
for (int i = 0; i < wantVM.size(); i++) {
VMs vMs = wantVM.get(i);
move(serverList.get(vMs.location), server, vMs);
}
}
}
releaseAfterLock();
}
public void releaseAfterLock() {
serverIn.clear();
serverOut.clear();
}
public Boolean findServerIn(Server curServer, VMs vm) {
// getServersChooseIn.sort(serverComparator.reversed());
for (Server server : serverList) {
if (serverOut.contains(server.ID)) {//腾出位置的虚拟机不允许再进
} else if (move(curServer, server, vm)) {
serverIn.add(server.ID);//记录用来进的虚拟机
return true;
}
}
return false;
}
/**
* ----------------------------正式操作----------------------------------------
*/
public static List<Server> serversChoose = new ArrayList<>();//severlist的副本,用来排序
public void start() {
serversChoose.addAll(serverList);
int N = serverList.size();
while (!isMissionCompleted()) {
int code = work(N);
if (code == -1)//任务结束
return;
else if (code == -2) {//出现死锁
openLock2();
if (check())
releaseResource();
startAfterOpenLock();
}
if (!check() && code != -2)
releaseResource();
else if (check())
releaseResource();
}
}
public int work(int N) {
//返回值-1 表示任务结束
//返回值-2 表示出现死锁
if (isMissionCompleted())
return -1;
//最多N步,死锁就跳出,遍历虚拟机
int instep = 0;//记录步数
serversChoose.sort(serverComparator);
for (Server server : serversChoose) {
List<VMs> isNotMyVM = server.isNotMyVM;
isNotMyVM.sort(vMsComparator);
for (int i = 0; i < isNotMyVM.size(); i++) {
VMs vMs = isNotMyVM.get(i);
if (move(server, serverList.get(vMs.destination), vMs))
instep++;
}
if (instep == N)
return 0;
}
if (isMissionCompleted())
return -1;
if (!changeFlag)//出现死锁
{
return -2;
}
return instep;
}
public static void main(String[] args) {
NewSolution newSolution = new NewSolution();
int N = scanner.nextInt();
int M = scanner.nextInt();
Server.input(N);
VMs.input(M);
newSolution.start();
//输出答案
// System.out.println(step - 1);
System.out.println(ans.size());
for (List<List<Integer>> an : ans) {
System.out.println(an.size());
for (List<Integer> list : an) {
for (Integer integer : list) {
System.out.print(integer + " ");
}
System.out.println();
}
}
}
}
策略总结:
解锁策略:
point:177002.276
破环:每次移动一半的待解锁(移动的次数不超过服务器的个数)
选择策略:for循环遍历服务器,放得下就放进去
point:181031.018
破环后不释放资源继续处理剩下的队列;
问题出在,破环时步数服务器已达到上限,再处理也是没用。
point:225130.514
解两遍锁,不排斥分配到目标位置
point:177229.358
假设待处理队列是服务器其总数的N倍,就解N/2遍锁
point:186444.959
假设待处理队列是服务器其总数的N倍,就解N遍锁
point: 223599.501
建立服务器的优先队列,每次选择服务器时都要更新,超时
point: 176838.023
在177002.276的基础上放弃优先队列,改用普通队列
point:176620
将服务器分为两类:只有出任务的服务器和其他服务器,出现死锁时,优先将造成死锁的任务倒到只有出任务的服务器里,
当死锁时 如果队列里的数字小于服务器个数,move 1/2
point:220181.018
当死锁时 如果队列里的数字小于服务器个数,就全部move 证明比移动一半差太多
point:176923.738
当死锁时 如果队列里的数字小于服务器个数,就move 1/3
point:176530.585
在176620的基础上(有这么个问题,先选择只有出任务的服务器,如果出任务的服务器里没地方放,也进不去其他服务器里找位置)
该掉了这个问题,变成优先出任务服务器,然后其他 都能找到
point:176530.585
把所有的队列改成list,方便索引
point:176776.811
充分利用step 不仅在出现死锁的时候解锁,在每一个step里的小step没用完的时候也解锁
point:292303.196
在上一步的基础上,每次解锁选择时,如果没有在只有出任务的服务器里找到课以移动的位置,就对服务器的剩余容量进行由大到小排序,
从最大的开始尝试放置;(程序出问题了,全超时)
问题出在,两个服务器之间的移动操作,不是更新的servers列表里服务器的值 程序崩溃了
point:快逃!
重构代码:
对于正常的一步中选择虚拟机开始执行的顺序:只入>有出有入>只出
死锁后服务器的选择:优先选择最拥挤的服务器开始出虚拟机
对于造成死锁后的虚拟机选择目标服务器的顺序:完成>只出>有出有入>只入
选中对应的服务器后选择服务器内虚拟机的顺序:1.是否要转出(要转出的排在前面)2.需要RAM越小越靠前 3.需要核心数越小越靠前
为了解决摇摆问题
解锁时从某一个虚拟机的isNotmyVM 里开始出虚拟机 出去的虚拟机在这个解锁步骤中不允许再移动
并且解锁步骤中的虚拟机只能出不能入。 为了把空间腾出来 。直到完成了一个解锁步骤N步或者所有被锁住的虚拟机都移动了一遍
point:174416.615
每个服务器内存储:1. 在本服务器内但不属于本服务器的虚拟机列表。每次出虚拟机时从这个列表里出 2.本该在本服务器但还没有到达的虚拟机名单
从前向后遍历服务器(按原本的ID顺序遍历,未排序),在约束条件以内能出则出
当出现死锁时:安置max(处于死锁状态的虚拟机个数/2,N) 台虚拟机
从前向后遍历服务器列表(按原本的ID顺序遍历,未排序)
对处于死锁中的虚拟机进行安置 某虚拟机 从 本服务器到B服务器 对于B服务器的选择也是从前向后遍历,放的下就放
安置过程中,保证若A服务器中有虚拟机出来,那么本次解锁过程中就不会有其他虚拟机进来
也就是说,1.解锁过程中没有虚拟机能回家 2.如果这台服务器有虚拟机出来,那么这台服务器在本趟解锁过程中只能用来出虚拟机(最大限度地腾出空间)
3.用来进虚拟机的服务器本次也只能用来进(如果这台服务器也出虚拟机的话,反正这虚拟机也回不了家,只会白白的增加内存移动消耗)
当没有虚拟机可以移动时,解锁结束(有使解锁结束的两种情况 1.达到了任务次数 2.所有的服务器都被贴上了出或入的标签 此时 出集合里没有能到入集合的虚拟机了)
此时一趟解锁结束。
解锁结束后,遍历“出”服务器集合中的服务器(这些服务器已经腾出空间了),每个服务器有一个本该在本服务器但还没有到达的虚拟机名单,让名单上的这些虚拟机回家
然后开始下一次遍历虚拟机。
point:173248.869
执行时按照代入RAM总和-本服务器剩余容量 降序, (最优)
point:176725.954
按照待出列表总和降序
point:175107.878
按照剩余容量
point:173289.99
在173248.869的基础上 解锁时也按照待入RAM总和-本服务器剩余容量 降序
point:175092.161
按期望进出差绝对值排序
point:
每动一步排个序,4个超时
point:
每动一个虚拟机排个序
point:174272.668
从前往后找和从后往前找同时进行
point:188372.414
不解锁的情况下。有四个未通过:10 13 15 23
point:173262.564
对每个虚拟机的需要内存进行降序,
point:173574
在上一步的基础上,先执行状态位为2的虚拟机 先让大的进来
调试解锁:
int times = Math.max(unCom / 5, N/2); 174237
int times = Math.max(unCom / 5, N); 174237
int times = Math.Min(unCom / 2+1, N); 174218.501
int times = Math.Min(unCom / 3+1, N); 174269.241
int times = Math.Min(unCom / 2+1, N/2); 174218.501(最优)
int times = Math.Min(unCom / 2+1, N/3); 174218.435
最优time下死锁时找剩余空间最大的进行入: 174176.711
最优time下死锁时找待进-left 最小的的进行入: 174181
point:173255.23
当前最优策略:
每个step 虚拟机排序 +虚拟机内vm排序 +解锁Math.Min(unCom / 2+1, N/2) 次
如果不对vm排序:173322.314,且时间还变多了
173275: 最优策略的基础下死锁时对xxx进行拍