2018年华为精英挑战赛初赛放置算法Java实现

放置阶段我用动态规划算法,java源码如下:

import java.util.ArrayList;
/**输入:服务器规格、虚拟机规格及预测数目、要优化资源维度
* 输出:放置结果和预测结果,以给定字符串形式返回
* @author wenj16
*/
public class DP {
/**输入参数
* bag 服务器类
* predictEcs 预测的虚拟机规格和数目,已经被处理成一个列表
* optResource 需要优化的资源维度,有CPU和MEM
*/
Bag bag;
ArrayList<Ecs> predictEcs;
String optResource;
/**中间变量和输出参数
* 注意:题目要求的是返回一个字符串数组
* c 动态规划每个点的最优值
* em 动态规划每个点消耗的内存
* record 记录每个点的上一个点
* cpu 动态规划每个点消耗的cpu,c、em、cpu可能编的有点多余,但是不影响
* x 记录每个物品是否被装进背包
* bagNum 开的背包个数
* secResult 返回第二阶段结果,是一个数列,每个元素存放一个背包装载情况
* totalNum 第一阶段虚拟机总预测数
* finalRes 最终结果
*/
int[][] c;
int[][] em;
int[][] record;
int[][] cpu;
int[] x;
int bagNum;
int totalNum;
ArrayList<String> secResult = new ArrayList<String>();
String[] finalRes;
/**我用一个一维数组backup备份预测阶段各个虚拟机的数目,用于修改第一阶段的预测结果
*
*/
int[] backup;
DP(ArrayList<Ecs> predictInfo,String optResource,Bag bag){
this.predictEcs=predictInfo;
this.optResource=optResource;
this.bag=bag;
}
/**动态规划解这个背包问题
* 可能会开若干个非常大的数组,用来迭代计算,内存占用比较大,要想办法优化
*/
public void init() {
//备份虚拟机预测数目到backup数组
backup=new int[predictEcs.size()];
for(int i=0;i<backup.length;i++) {
backup[i]=predictEcs.get(i).num;
}
//将内存统一除以1024
for(int i=0;i<predictEcs.size();i++) {
predictEcs.get(i).eme/=1024;
}
//计算背包cpu
int maxCPU= bag.cpuCap;
//计算背包内存
int maxEM=bag.emeCap ;
//-------------------------------------------_-_--------------------优化cpu-------------------------------
if(this.optResource.equalsIgnoreCase("CPU")) {
//要优化资源维度是cpu
boolean flag=true;
while(flag==true) {
flag=false;
//动态规划
//计算预测的虚拟机数目
int goodNum=0;
for(int i=0;i<predictEcs.size();i++) {
goodNum+=predictEcs.get(i).num;
}
//初始化矩阵
c=new int[goodNum+1][maxCPU+1];
em=new int[goodNum+1][maxCPU+1];
//记录上一个点
record=new int[goodNum+1][maxCPU+1];
//依次遍历每个虚拟机
for(int i=1;i<=goodNum;i++) {
//计算i属于第几个flavor
int type=0;
int sum=0;
for(int j=0;j<predictEcs.size();j++) {
sum+=predictEcs.get(j).num;
if(i<=sum) {
type=j;//j=0 属于flavor1
break;
}
}
//依次遍历每个cpu
for(int j=1;j<=maxCPU;j++) {
//状态转移方程
if(predictEcs.get(type).cpu <= j) {//背包可能装下j
if(predictEcs.get(type).cpu +c[i-1][j - predictEcs.get(type).cpu] > c[i-1][j]) {//装下价值更大
if(predictEcs.get(type).eme + em[i-1][j - predictEcs.get(type).cpu] <= maxEM) {//内存也不违背
c[i][j] = predictEcs.get(type).cpu + c[i-1][j - predictEcs.get(type).cpu];
em[i][j] = predictEcs.get(type).eme + em[i-1][j - predictEcs.get(type).cpu];
record[i][j]=j - predictEcs.get(type).cpu;
}else if(predictEcs.get(type).eme + em[i-1][j - predictEcs.get(type).cpu] > maxEM) {//能够装下但是内存会违背
c[i][j] = c[i-1][j];
em[i][j] = em[i-1][j];
record[i][j]=j;
for(int k=j-1;k>=0;k--) {//遍历看上一步哪个点内存不违背而且价值更大
if(em[i-1][k]+predictEcs.get(type).eme<=maxEM && c[i-1][k]+predictEcs.get(type).cpu>c[i-1][j]) {
c[i][j]=c[i-1][k]+predictEcs.get(type).cpu;
em[i][j]=em[i-1][k]+predictEcs.get(type).eme;
record[i][j]=k;//记录下这个位置
break;
}
}
}
}
else {
c[i][j] = c[i-1][j];
em[i][j] = em[i-1][j];
record[i][j]=j;
}
}else {
c[i][j] = c[i-1][j];
em[i][j] = em[i-1][j];
record[i][j]=j;
}
}
}
//反向逆推获得背包物品
boolean[] x = new boolean[goodNum+1];
int cpuTemp=maxCPU;
for(int i=goodNum;i>0;i--) {
if(record[i][cpuTemp]==cpuTemp)
x[i]=false;
else {
x[i]=true;
//计算i属于第几个flavor
int type=0;
int sum=0;
for(int j=0;j<predictEcs.size();j++) {
sum+=predictEcs.get(j).num;
if(i<=sum) {
type=j;
break;
}
}
cpuTemp=record[i][cpuTemp];
}
}
//返回一个背包装载结果
String oneBagResult = "";
int[] flavorNums=new int[predictEcs.size()];
int index=1;
for(int i=0;i<predictEcs.size();i++) {
int temp =predictEcs.get(i).num;
if(temp!=0) {
int total=0;
for(int j=index;j<index+temp;j++) {
if(x[j]==true) {
total+=1;
//资源扣减
predictEcs.get(i).num--;
}
}
index+=temp;
if(total!=0) {
oneBagResult = oneBagResult + predictEcs.get(i).name + " " + total + " ";//flavor1 1 flavor2 0 flavor3 0
flavorNums[i]=total;
}
}
}
//将背包放入列表
secResult.add(oneBagResult);
bagNum++;
//判断剩下资源有足够一个背包的资源,有就再开一个
boolean flag2=true;
while(flag2==true) {
for(int i=0;i<predictEcs.size();i++) {
if(predictEcs.get(i).num<flavorNums[i]) {
flag2=false;
break;
}
}
if(flag2==true) {
secResult.add(new String(oneBagResult));
bagNum++;
for(int i=0;i<predictEcs.size();i++) {
predictEcs.get(i).num-=flavorNums[i];
}
}
}
/**计算背包资源总数,如果小于一个背包的15%,修改备份backup中的预测数,同时将所有虚拟机设置为0
* 同时还得重新计算预测虚拟机总数
*/
double restResources=0;
for(int i=0;i<predictEcs.size();i++) {
restResources=restResources+predictEcs.get(i).num*predictEcs.get(i).cpu;
}
if(restResources<0.15*bag.cpuCap) {
for(int i=0;i<backup.length;i++) {
backup[i]-=predictEcs.get(i).num;//修改第一阶段预测数目
predictEcs.get(i).num=0;//该虚拟机数目设置为0
}
//重新计算第一阶段虚拟机总的预测数目
totalNum=0;
for(int i=0;i<backup.length;i++) {
totalNum+=backup[i];
}
}
//下一次背包循环
for(int i= 0;i<predictEcs.size();i++) {
if(predictEcs.get(i).num!=0) {
flag=true;
break;
}
}
}
//-------------------------------------------_-_--------------------优化内存------------------------------
}else if(this.optResource.equalsIgnoreCase("MEM")) {
//计算背包内存
maxEM=bag.emeCap;
boolean flag=true;
while(flag==true) {
flag=false;
//计算预测的虚拟机数目
int goodNum=0;
for(int i=0;i<predictEcs.size();i++) {
goodNum+=predictEcs.get(i).num;
}
//初始化矩阵
c=new int[goodNum+1][maxEM+1];
cpu=new int[goodNum+1][maxEM+1];
record=new int[goodNum+1][maxEM+1];
//依次遍历每个虚拟机
for(int i=1;i<=goodNum;i++) {
//计算i属于第几个flavor
int type=0;
int sum=0;
for(int j=0;j<predictEcs.size();j++) {
sum+=predictEcs.get(j).num;
if(i<=sum) {
type=j;
break;
}
}
//依次遍历每个eme
for(int j=1;j<=maxEM;j++) {
if(predictEcs.get(type).eme <= j) {

if(predictEcs.get(type).eme +c[i-1][j - predictEcs.get(type).eme] > c[i-1][j]) {

if(predictEcs.get(type).cpu + cpu[i-1][j - predictEcs.get(type).eme] <= maxCPU) {
c[i][j] = predictEcs.get(type).eme + c[i-1][j - predictEcs.get(type).eme ];
cpu[i][j] = predictEcs.get(type).cpu + cpu[i-1][j - predictEcs.get(type).eme];
record[i][j]=j - predictEcs.get(type).eme;

}else if(predictEcs.get(type).cpu + cpu[i-1][j - predictEcs.get(type).eme] > maxCPU) {
c[i][j] = c[i-1][j];
cpu[i][j] = cpu[i-1][j];
record[i][j]=j;
for(int k=j-1;k>=0;k--) {
if(cpu[i-1][k]+predictEcs.get(type).cpu<=maxCPU && c[i-1][k]+predictEcs.get(type).eme>c[i-1][j]) {
c[i][j]=c[i-1][k]+predictEcs.get(type).eme;
cpu[i][j]=cpu[i-1][k]+predictEcs.get(type).cpu;
record[i][j]=k;
break;
}
}
}
}

else {
c[i][j] = c[i-1][j];
cpu[i][j] = cpu[i-1][j];
record[i][j]=j;
}

}else {
c[i][j] = c[i-1][j];
cpu[i][j] = cpu[i-1][j];
record[i][j]=j;
}
}

}
//反向逆推获得背包物品
boolean[] x = new boolean[goodNum+1];
int emeTemp=maxEM;
for(int i=goodNum;i>0;i--) {
if(record[i][emeTemp]==emeTemp)//bug鍦ㄨ繖
x[i]=false;
else {
x[i]=true;
//计算i属于第几个flavor
int type=0;
int sum=0;
for(int j=0;j<predictEcs.size();j++) {
sum+=predictEcs.get(j).num;
if(i<=sum) {
type=j;
break;
}
}

emeTemp=record[i][emeTemp] ;
}
}
//返回一个背包装载结果
String oneBagResult = "";
int[] flavorNums=new int[predictEcs.size()];
int index=1;
for(int i=0;i<predictEcs.size();i++) {
int temp =predictEcs.get(i).num;
if(temp!=0) {
int total=0;
for(int j=index;j<index+temp;j++) {
if(x[j]==true) {
total+=1;
//资源扣减
predictEcs.get(i).num--;
}
}
index+=temp;
if(total!=0) {
oneBagResult = oneBagResult + predictEcs.get(i).name + " " + total + " ";
flavorNums[i]=total;
}
}
}
//将背包放入列表
secResult.add(oneBagResult);//bug 瑙e嚭涓�涓┖鑳屽寘
bagNum++;
//剩余资源是否有相同背包,有就新开同样的背包并做资源扣减
boolean flag2=true;
while(flag2==true) {
for(int i=0;i<predictEcs.size();i++) {
if(predictEcs.get(i).num<flavorNums[i]) {
flag2=false;
break;
}
}
if(flag2==true) {
secResult.add(new String(oneBagResult));
bagNum++;
for(int i=0;i<predictEcs.size();i++) {
predictEcs.get(i).num-=flavorNums[i];
}
}
}
/**计算背包资源总数,如果小于一个背包的15%,修改备份backup中的预测数,同时将所有虚拟机设置为0
* 同时还得重新计算预测虚拟机总数
*/
double restResources=0;
for(int i=0;i<predictEcs.size();i++) {
restResources=restResources+predictEcs.get(i).num*predictEcs.get(i).eme;//内存的情况单位得注意一下
}
if(restResources<0.15*bag.emeCap) {
for(int i=0;i<backup.length;i++) {
backup[i]-=predictEcs.get(i).num;//修改第一阶段预测数目
predictEcs.get(i).num=0;//该虚拟机数目设置为0
}
//重新计算第一阶段虚拟机总的预测数目
totalNum=0;
for(int i=0;i<backup.length;i++) {
totalNum+=backup[i];
}
}
//下一个背包循环
for(int i= 0;i<predictEcs.size();i++) {
if(predictEcs.get(i).num!=0) {
flag=true;
break;
}
}

}
}
System.out.println("zai看Ecses");
for(int i=0;i<predictEcs.size();i++) {
System.out.println(predictEcs.get(i).name+" "+predictEcs.get(i).cpu+" "+predictEcs.get(i).eme+" "+predictEcs.get(i).num+" ");
}
}
/**返回结果是题目要求的字符串,格式是这样的:
6
flavor5 3
flavor10 2
flavor15 1
(备注:如果某种虚拟机规格的预测结果为零,即对应写0)
4
1 flavor5 2
2 flavor5 1 flavor10 1
3 flavor15 1
4 flavor10 1
* @return 返回两阶段的结果
*/
public String[] getResult() {
int listNum=1+predictEcs.size()+1+bagNum+1;
finalRes=new String[listNum];
//第一阶段结果
finalRes[0]=""+totalNum;
for(int i=1;i<1+predictEcs.size();i++) {
finalRes[i]=predictEcs.get(i-1).name+" "+backup[i-1];
}
//第二阶段结果
finalRes[predictEcs.size()+1]=" ";
finalRes[predictEcs.size()+2]=""+bagNum;
for(int i=predictEcs.size()+3;i<listNum;i++) {
int order=i-(predictEcs.size()+3)+1;
String aBag=secResult.get(order-1);
finalRes[i]=order+" "+aBag;
}
return finalRes;
}

}

猜你喜欢

转载自www.cnblogs.com/wenj17/p/9243237.html