Browse Source

复赛。

liuyuqi-dellpc 5 years ago
parent
commit
cea168e3de

+ 235 - 0
docs/复赛赛题.md

@@ -0,0 +1,235 @@
+# 阿里巴巴全球调度算法大赛
+
+https://tianchi.aliyun.com/competition/introduction.htm?spm=5176.100066.0.0.6acd33afqgdgz0&raceId=231663
+
+在初赛的基础上,增加了以下内容:
+
+## 在线任务的迁移限制 
+
+迁移将更遵循实际,采取多轮并发执行,每一轮并发迁移为多个迁移命令同时执行,一个迁移命令将采取先新建后删除的方式。例如:
+
+```
+1,inst1,A
+1,inst2,A
+2,inst3,B
+```
+
+为_两阶段_迁移计划。在第一轮中,inst1和inst2迁移到了机器A上,这个是分两个阶段执行的:第一阶段,将同时在机器A上新建inst1、inst2实例,新建成功后再将inst1、inst2在原来所属的机器删除旧实例,并执行下一轮(inst3到机器B)。
+
+若出现迁移失败,则直接退出在线任务调度阶段。
+
+**迁移限制**:每轮迁移没有命令数量限制,但限制轮数小于等于k(暂定k=3)。
+
+## 离线任务调度 
+
+初赛仅涉及在线任务的调度,实际场景我们常常需要在离线任务混部才能获得最高的资源利用率。
+
+定义一个离线任务\(job\_info.x.csv\)为:
+
+```
+离线任务id, cpu占用, mem占用, 实例数, 执行时间, 前驱任务id列表
+
+```
+
+其中
+
+* 执行时间的单位为分钟。对应到在线任务的cpu/mem曲线的98个点,分别代表该在线任务在\[0, 15\)、\[15, 30\)、...、\[1455, 1470\)时刻的资源用量
+* 每个离线任务的最早启动时刻,必须大于其所有前驱任务的所有实例执行完成的时刻t
+* 在一个完整周期\[0, 1470\)内,所有离线任务的所有实例必须全部被执行完成离线任务不允许迁移
+  * 对于一个离线任务,只有当其所有的instance都执行完毕,这个离线任务才可以被认为是执行完成
+* 
+所以最终的提交结果分为两个部分:
+
+* 在线任务阶段:
+  `迁移轮数, 在线任务实例id, 目标宿主机id`
+* 离线任务阶段:
+  `离线任务id, 目标宿主id, 启动时刻, 启动实例数量`
+
+一旦开始离线任务的调度,便不允许继续进行在线任务的调度。
+
+## 评分修改
+
+原评分公式中的a,由a=10,修改为
+
+```
+a=(1+该机器上部署的在线任务inst数量)
+
+```
+
+## 赛题数据说明
+
+复赛有a, b, c, d, e一共5份赛题,每份赛题包含5份数据,其中:
+
+* app\_interference.csv、app\_resources.csv为5份赛题的公用数据
+* instance\_deploy.x.csv、job\_info.x.csv、machine\_resources.x.csv为第x份赛题对应的独有数据
+
+最终提交结果按a,b,c,d,e的顺序拼接,以\#分割。
+
+下面是一个例子:
+
+```
+1, inst1, machine_a
+task_1, machine_a, 10, 31
+#
+1, inst1, machine_b
+2, inst2, machine_c
+task_1, machine_c, 10, 28
+
+```
+
+复赛排行榜中,选手的分数为五份赛题答案得分的总和。
+
+## 为复赛胜出的准备
+
+参加决赛资格由复赛成绩决定,复赛成绩的主要依据是排名,但也包括赛事导师对排名靠前选手的线下代码审核。为了让选手更好的准备复赛之后的提交,我们有如下建议:
+
+* 请按照我们初赛描述中推荐的方式整合您的代码 \(link\)
+* 请准备一个简短报告,至少包括
+  * 您对赛题的理解和解题思路
+  * 最后得出方案的计算环境的配置和计算时间(硬件、软件、时间等)
+  * 任何您想说的话
+* 复赛结束时(2018.09.07),我们会按照选手提交的复赛成绩,结合初赛时候的一些表现,在复赛结束时要求指定的选手提交代码和报告。如果不在规定的时间提交,视作放弃,但我们真诚的希望出色的选手可以和我们分享您的成果。如果有选手没有被邀请提交代码和报告,我们也欢迎您毛遂自荐,我们的导师会看您的代码和报告,并给出答复意见。
+
+**初赛赛题描述**
+
+重要更新:
+
+1. 7月26日,我们对初赛赛制进行了更新,具体的:
+
+    \* 我们新开放了新一个版本的数据(Data\_B),之前的数据称之为Data\_A,作用于初赛
+
+    \*Data\_B和Data\_A描述的是两个独立的问题。Data\_B和Data\_A的格式一致,只有数值不同,主要目的是为了防止参赛选手设计针对Data\_A过拟合的算法(经我们测试,通用算法无需修改就可以算出合法的解,但Data\_B有更多优化空间)
+
+    \* 由于添加Data\_B导致的提交结果的变化,见下面的“提交结果”部分。简单的说,新的提交方式中,参赛选手还是提交一份答案,其格式为
+
+<Data\_A的解>
+
+        \#
+
+<Data\_B的解>
+
+    \* 评分机制会有相应修改,并在7月30日作用于排行榜。修改后的评分公式为:final\_score = 0.5\*\(score\(Data\_A\)+score\(Data\_B\)\);针对Data\_A和Data\_B的评分机制score函数,与更新前保持一致,具体见“执行与评分(初赛)”部分;进入复赛的条件保持不变(见“赛制介绍”)
+
+    \* 对于此次更新给各位选手带来的不便我们感到十分抱歉,但会尽力为选手提供最好的参赛体验。更多关于赛题更新的说明可以参考:[https://tianchi.aliyun.com/forum/new\_articleDetail.html?spm=5176.8366600.0.0.52f3311fT3qWk6&raceId=231663&postsId=5802](https://tianchi.aliyun.com/forum/new_articleDetail.html?spm=5176.8366600.0.0.52f3311fT3qWk6&raceId=231663&postsId=5802)
+
+2. 赛题在6月11日有一次更新(赛题描述和数据),6月7日上线的赛题已经彻底下线,请所有参赛同学以你当前看到的版本赛题为准,7月初开始的评测工作将以目前版本为准。由此给各位选手带来的不便,深感歉意。但我们相信,目前这个版本的赛题无论对于初学者还是资深的研究者,都是很有趣的。
+
+## 1赛题描述
+
+7月26日添加了一份新的数据(Data\_B\_xxxx),下面的赛题描述同时适用于之前版本的数据(Data\_A\_xxxx)和更新的数据(Data\_B\_xxxx),基于二者共同作用的评分机制见上面的“重要更新”部分。注意,Data\_A和Data\_B描述的是两个独立的问题,只是格式一样。
+
+共约6K台宿主机(machine),包含了若干种型号,约68K个任务实例(instance),其中一部分已经部署在宿主机上,还有一部分没有被部署。
+
+要求: 设计调度算法,在满足要求约束的前提下,通过将全部未被调度的任务实例调度到宿主机上以及腾挪部分已经部署的实例的方式,得到最优的部署方案。最优部署方案指实际使用宿主机数目尽可能少,且宿主机负荷不能过高。请参考“执行与评分(初赛)”部分来获得更多关于最优部署方案的说明。
+
+在解释数据格式、约束条件之前,为防止概念混淆,先给出几个定义,全文出现的概念以此定义为准。
+
+实例(instance):一个实例是可以被调度到一个机器上的对象,在实际生产中,一个实例可以是一个docker容器
+
+应用分组(App):一个应用分组包括很多实例(instance)。属于同一个App下的所有实例,具备相同的约束条件。一个实例能且只能属于一个应用分组
+
+机器(Machine):机器是集群中的服务器,一个实例被可以被调度到一个机器上
+
+###  1.1约束描述
+
+任务实例到宿主机的分配,需要满足下列约束:
+
+·每个实例都标明了CPU、memory、disk此3个维度的资源需求,其中CPU、memory以分时占用曲线的形式给出,在任意时刻,任意一个宿主机A上,所有部署在宿主机A上的实例的任意资源都不能超过宿主机A的该资源容量
+
+·另外还有P、M、PM三类资源,定义了应用实例的重要程度,任意一台宿主机上的部署数目不能超过该类型宿主机能够容纳的重要应用数目上限
+
+·混部集群时刻处于复杂的干扰环境中,所以我们需要满足一些规避干扰约束,一条规避干扰约束被描述为<APP\_A, APP\_B, k>,代表若一台宿主机上存在APP\_A类型的实例,则最多能部署k个APP\_B类型的实例。注意,k可能为0。APP\_A和APP\_B也可能代表同一个APP(e.g. <APP\_A, APP\_A, k>),代表同一台机器上最多可以部署的该APP的实例的数目
+
+### 1.2数据描述
+
+问题一共包含四份数据表:instance\_deploy.csv, app\_resources.csv, machine\_resources.csv, app\_interference.csv
+
+**instance\_deploy.csv
+**
+o实例id
+
+o实例所属应用
+
+o实例所属宿主机:
+
+§注:当前未分配的实例,实例所属宿主机列为空
+
+**·app\_resources.csv
+**
+o应用id
+
+ocpu分时占用曲线(每个点由< \| >隔开)
+
+omem分时占用曲线(每个点由< \| >隔开)
+
+odisk申请量(标量)
+
+oP
+
+oM
+
+oPM
+
+**·machine\_resources.csv
+**
+o宿主机id
+
+ocpu规格
+
+omem规格
+
+odisk规格
+
+oP上限
+
+oM上限
+
+oPM上限
+
+**·app\_interference.csv
+**
+o应用id1
+
+o应用id2
+
+o最大部署量
+
+### 1.3结果提交
+
+#### 1.3.5推荐的复赛提交格式(暂定)
+
+在第二阶段比赛(复赛)接近尾声时,我们会要求排行榜排名前10的队伍提交针对复赛题目的计算出迁移方案的代码,进行线下评测。迁移方案和线下评测的标准会在“评价标准”中说明,如果参赛队伍不能提供代码、或者提交代码与结果不匹配,相应的排行榜成绩无效。
+
+注1:提交文件夹结构
+
+·project
+
+·\|--README.md
+
+·\|--data
+
+·\|--code
+
+·   \|-- main.py or main.ipynb or <其它语言代码>
+
+·\|--submit
+
+·   \|-- submit\_20180203\_040506.csv
+
+注2:提交文件文件名代码
+
+·\# java for example
+
+·import datetime
+
+·data.to\_csv\(\("../submit/submit\_"+datetime.datetime.now\(\).strftime\('%Y%m%d\_%H%M%S'\) + ".csv"\), header=None, index=False\)
+
+### 1.4你可以用这份数据设计其它算法
+
+下面的要求不是比赛的一部分,但同样是数据中心资源调度关心的目标。爱好者可以根据这份数据设计以下面需求之一为目的的调度算法。我们十分欢迎您与我们交流您的想法!
+
+1.同样是上述数据和问题,设计在线调度算法。所谓在线调度算法,是待调度的任务顺序地被调度器调度,而调度器不知道待调度任务序列中靠后的任务的信息。实践中,在线算法只能接近,但很难达到离线算法的效果。
+
+2.让算法更robustness。实际环境中,大量数据为建模预估产生的模型化数据,例如赛题中的cpu, mem分时占用曲线,如何在预估数据存在偏差的前提进行问题求解,或者如何在已知决策模型的前提下调整预估方法,也是充满挑战的问题。
+
+3.其它任何你能想到的使用这份数据可以设计的问题和算法。如果你对这个有兴趣,我们相信你会对我们第二阶段的比赛更加有兴趣,请保持关注并一定参加我们的正式比赛!

+ 472 - 323
java/com/aliyun/tianchi/mgr/evaluate/evaluate/file/evaluator/AlibabaSchedulerEvaluatorRun.java

@@ -1,339 +1,488 @@
 package com.aliyun.tianchi.mgr.evaluate.evaluate.file.evaluator;
 
-
-import java.util.List;
-import java.util.ArrayList;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileReader;
-import java.io.InputStream;
-import java.io.ByteArrayInputStream;
-import java.io.BufferedReader;
-
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.io.InputStream;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.util.List;
+import java.io.InputStreamReader;
 import java.util.ArrayList;
-import java.util.Map;
 import java.util.HashMap;
-import com.google.common.base.Charsets;
-import org.apache.commons.lang3.tuple.Pair;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+
+import com.google.common.base.Charsets;
 
 /**
- * Created by mou.sunm on 2018/07/02.
+ * Created by mou.sunm on 2018/08/19.
  */
 public class AlibabaSchedulerEvaluatorRun {
-    // 参数
-    public static final double  alpha             = 10.;
-    public static final double  beta              = 0.5;
-    public static final int     T                 = 98;
-    public static final int     EXEC_LIMIT        = 100000;
-    
-    // 静态数据
-    private int                     n;                  // app数
-    private int                     N;                  // inst数
-    private int                     m;                  // machine数
-    private int                     k;                  // 资源种类
-    private List<Integer>           cpuIter;            // T个时刻的cpu资源
-    private Map<String, Integer>    appIndex;
-    private Map<String, Integer>    machineIndex;
-    private String[]                apps;
-    private String[]                machines;
-    private Map<String, Integer>    inst2AppIndex;
-    private double[][]              appResources;  
-    private double[][]              machineResources;  
-    private Map<Integer, Integer>[] appInterference;
-    
-    // 动态数据
-    private Map<String, Integer>       inst2Machine;
-    private double[][]                 machineResourcesUsed;
-    private Map<Integer, Integer>[]    machineHasApp;
-    
-    protected double evaluate(BufferedReader bufferedReader) throws IOException {
-        double costs = 0.;
-        try {
-            /** 读取执行数据 */
-            List<Pair<String, Integer>> execs = new ArrayList<Pair<String, Integer>>();
-            for (String line = bufferedReader.readLine(); line != null; line = bufferedReader.readLine()) {
-                String[] pair = line.split(",", -1);
-                if (pair.length != 2) throw new Exception("Invaild solution file");
-                if (!inst2AppIndex.containsKey(pair[0]) || !machineIndex.containsKey(pair[1]))
-                    throw new Exception("Invaild solution file");
-                execs.add(new ImmutablePair(pair[0], machineIndex.get(pair[1])));
-            }
-            /** 逐行执行 */
-            int iter = 0;
-            for (Pair<String, Integer> exec : execs) {
-                iter++;
-                //if (iter > EXEC_LIMIT) {
-                    //System.out.println("超过EXECUTION LIMIT(" + EXEC_LIMIT+"), 执行中断");
-                    //break;
-                //}
-                String  inst        = exec.getLeft();
-                Integer machineIt   = exec.getRight();
-                pickInstance(inst); // 先将inst从当前所属的machine删除
-                String msg = toMachine(inst, machineIt);
-                if (!msg.equals("success")) {
-                    System.out.println("执行中断于第" + iter + "行: " + msg);
-                    break; // 执行失败立即退出
-                }
-            }
-            /** 计算终态得分 */
-            // 检查inst是否全部放入machine
-            for (String inst : inst2AppIndex.keySet())
-                if (!inst2Machine.containsKey(inst)) throw new Exception("instance未全部分配");
-            // 检查machine的终态
-            for (int j = 0; j < m; j++) {
-                Map<Integer, Integer> hasApp = machineHasApp[j];
-                if (hasApp.size() == 0) continue;
-                // 检查互斥条件
-                for (Integer conditionalApp : hasApp.keySet()) {
-                    if (hasApp.get(conditionalApp) <= 0) throw new Exception("[DEBUG 1]Stupid Judger.");
-                    for (Integer checkApp : appInterference[conditionalApp].keySet()) {
-                        if (hasApp.containsKey(checkApp)) {
-                            if (hasApp.get(checkApp) > appInterference[conditionalApp].get(checkApp))
-                                throw new Exception("终态存在干扰冲突");
-                        }
-                    }
-                }
-                // 检查资源限制
-                for (int i = 0; i < k; i++)
-                    if (dcmp(machineResourcesUsed[j][i] - machineResources[j][i]) > 0)
-                        throw new Exception("终态存在资源过载");
-                // 技术得分
-                for (Integer t : cpuIter) {
-                    double usage = machineResourcesUsed[j][t] / machineResources[j][t];
-                    costs += 1. + alpha*(Math.exp(Math.max(0., usage - beta)) - 1.);
-                }
-            }
-            costs /= T;
-        } catch (Exception e) {
-            System.out.println(e.getMessage());
-            //e.printStackTrace();
-            costs = 1e9;
-        }
-        return costs;
-    }
-    
-    // 读取数据
-    protected void init(BufferedReader bufferedReader) throws IOException {
-        /* Preprocessing: cat *.csv to one file as:
-               n
-               app_resources.csv
-               m
-               machine_resources.csv
-               N
-               instance_deploy.csv
-               iterference_cnt
-               app_interference.csv
-            judge framework 
-        */
-        /** cpuIter */
-        cpuIter = new ArrayList<Integer>();
-        for (int i = 0; i < T; i++)
-            cpuIter.add(i);
-        /** Read app_resources */
-        n = Integer.parseInt(bufferedReader.readLine());
-        apps = new String[n];
-        for (int i = 0; i < n; i++) {
-            // appId,resources
-            String line = bufferedReader.readLine();
-            String[] parts = line.split(",", -1);
-            List<Double> resources = new ArrayList<Double>();
-            for (String x : parts[1].split("\\|", -1))
-                resources.add(Double.parseDouble(x));
-            for (String x : parts[2].split("\\|", -1))
-                resources.add(Double.parseDouble(x));
-            for (int j = 3; j < parts.length; j++)
-                resources.add(Double.parseDouble(parts[j]));
-            if (i == 0) {
-                k = resources.size();
-                appIndex = new HashMap<String, Integer>();
-                appResources = new double[n][k];
-            }
-            if (k != resources.size()) 
-                throw new IOException("[DEBUG 2]Invaild problem");
-            if (appIndex.containsKey(parts[0]))
-                throw new IOException("[DEBUG 3]Invaild problem");
-            appIndex.put(parts[0], i);
-            apps[i] = parts[0];
-            for (int j = 0; j < k; j++)
-                appResources[i][j] = resources.get(j);
-        }
-        /** Read machine_resources*/
-        m = Integer.parseInt(bufferedReader.readLine());
-        machineResources = new double[m][k];
-        machineResourcesUsed = new double[m][k];
-        machineIndex = new HashMap<String, Integer>();
-        machineHasApp = new Map[m];
-        machines = new String[m];
-        for (int i = 0; i < m; i++) {
-            // machineId,resources
-            String line = bufferedReader.readLine();
-            String[] parts = line.split(",", -1);
-            if (machineIndex.containsKey(parts[0]))
-                throw new IOException("[DEBUG 4]Invaild problem");
-            machineIndex.put(parts[0], i);
-            machines[i] = parts[0];
-            machineHasApp[i] = new HashMap<Integer, Integer>();
-            double cpu = Double.parseDouble(parts[1]);
-            double mem = Double.parseDouble(parts[2]);
-            for (int j = 0; j < T; j++) {
-                machineResources[i][j]   = cpu;
-                machineResources[i][T+j] = mem;
-            }
-            for (int j = 3; j < parts.length; j++)
-                machineResources[i][2*T + j - 3] = Double.parseDouble(parts[j]);
-            for (int j = 0; j < k; j++)
-                machineResourcesUsed[i][j] = 0.;
-        }
-        /** Read instance_deploy */
-        N = Integer.parseInt(bufferedReader.readLine());
-        inst2AppIndex = new HashMap<String, Integer>();
-        inst2Machine  = new HashMap<String, Integer>();
-        for (int i = 0; i < N; i++) {
-            String line = bufferedReader.readLine();
-            String[] parts = line.split(",", -1);
-            if (inst2AppIndex.containsKey(parts[0]))
-                throw new IOException("[DEBUG 5]Invaild problem");
-            if (!appIndex.containsKey(parts[1]))
-                throw new IOException("[DEBUG 6]Invaild problem");
-            inst2AppIndex.put(parts[0], appIndex.get(parts[1]));
-            if (!"".equals(parts[2])) {
-                if (!machineIndex.containsKey(parts[2]))
-                    throw new IOException("[DEBUG 7]Invaild problem");
-                toMachine(parts[0], machineIndex.get(parts[2]), false);
-            }
-        }
-        /** Read app_interference */
-        int icnt = Integer.parseInt(bufferedReader.readLine());
-        appInterference = new Map[n];
-        for (int i = 0; i < n; i++)
-            appInterference[i] = new HashMap<Integer, Integer>();
-        for (int i = 0; i < icnt; i++) {
-            String line = bufferedReader.readLine();
-            String[] parts = line.split(",", -1);
-            if (!appIndex.containsKey(parts[0]) || !appIndex.containsKey(parts[1]))
-                throw new IOException("[DEBUG 8]Invaild problem");
-            int app1 = appIndex.get(parts[0]);
-            int app2 = appIndex.get(parts[1]);
-            int limit = Integer.parseInt(parts[2]);
-            Map<Integer, Integer> inter = appInterference[app1];
-            if (inter.containsKey(app2))
-                throw new IOException("[DEBUG 9]Invaild problem");
-            if (app1 == app2) limit += 1; //self-interference +1 here
-            inter.put(app2, limit);
-        }
-    }
-    
-    
-    private String toMachine(String inst, int machineIt)
-    {
-        return toMachine(inst, machineIt, true);
-    }
-    private String toMachine(String inst, int machineIt, boolean doCheck)
-    {
-        int appIt       = inst2AppIndex.get(inst);
-        Map<Integer, Integer> hasApp = machineHasApp[machineIt];
-        if (doCheck) {
-            // 检查互斥规则
-            int nowHas = 0;
-            if (hasApp.containsKey(appIt))
-                nowHas = hasApp.get(appIt);
-            for (Integer conditionalApp : hasApp.keySet()) {
-                if (hasApp.get(conditionalApp) <= 0) continue;
-                if (!appInterference[conditionalApp].containsKey(appIt)) continue;
-                if (nowHas + 1 > appInterference[conditionalApp].get(appIt)) {
-                    return "App Interference, inst: " + inst + ", "
-                        + apps[conditionalApp] + " -> " + apps[appIt] + ", "
-                        + (nowHas + 1) + " > " + appInterference[conditionalApp].get(appIt); 
-                }
-            }
-            for (Integer checkApp : hasApp.keySet()) {
-                if (!appInterference[appIt].containsKey(checkApp)) continue;
-                if (hasApp.get(checkApp) > appInterference[appIt].get(checkApp)) {
-                    return "App Interference, inst: " + inst + ", "
-                        + apps[appIt] + " -> " + apps[checkApp] + ", "
-                        + (nowHas + 1) + " > " + appInterference[appIt].get(checkApp); 
-                }
-            }
-            // 检查资源限制
-            for (int i = 0; i < k; i++)
-                if (dcmp(machineResourcesUsed[machineIt][i] + appResources[appIt][i] - machineResources[machineIt][i]) > 0) 
-                    return "Resource Limit: inst: " + inst + ", " 
-                        + "machine: " + machines[machineIt] + ", app: " + apps[appIt] + ", resIter: " + i + ", "
-                        + machineResourcesUsed[machineIt][i] + " + " + appResources[appIt][i] + " > " + machineResources[machineIt][i];
-        }
-        // 将inst放入新的machine
-        inst2Machine.put(inst, machineIt);
-        if (!hasApp.containsKey(appIt))
-            hasApp.put(appIt, 0);
-        hasApp.put(appIt, hasApp.get(appIt) + 1);
-        for (int i = 0; i < k; i++)
-            machineResourcesUsed[machineIt][i] += appResources[appIt][i];
-        
-        return "success";
-    }
-    private void pickInstance(String inst)
-    {
-        if (!inst2Machine.containsKey(inst)) return;
-        int appIt       = inst2AppIndex.get(inst);
-        int fromMachine = inst2Machine.get(inst);
-        // 更新machineHasApp
-        Map<Integer, Integer> fromHasApp = machineHasApp[fromMachine];
-        fromHasApp.put(appIt, fromHasApp.get(appIt) - 1);
-        if (fromHasApp.get(appIt) <= 0)
-            fromHasApp.remove(appIt);
-        // 更新machineResourcesUsed
-        for (int i = 0; i < k; i++)
-            machineResourcesUsed[fromMachine][i] -= appResources[appIt][i];
-        // 更新inst2Machine
-        inst2Machine.remove(inst);
-    }
+	// 参数
+	public static final double beta = 0.5;
+	public static final int T2 = 98 * 15;
+	public static final int EXEC_LIMIT = 3;
+
+	private List<Problem> problems;
+
+	// 主函数
+	public static void main(String[] args) throws Exception {
+		if (args.length != 6 && args.length != 2) {
+			System.err.println("传入参数有误,使用方式为:");
+			System.err.println(
+					"\t\tjava -cp xxx.jar com.aliyun.tianchi.mgr.evaluate.evaluate.file.evaluator.AlibabaSchedulerEvaluatorRun app_resources.csv machine_resources.csv instance_deploy.csv app_interference.csv job_info.csv result.csv");
+			System.err.println("或:");
+			System.err.println(
+					"\t\tjava -cp xxx.jar com.aliyun.tianchi.mgr.evaluate.evaluate.file.evaluator.AlibabaSchedulerEvaluatorRun judge.csv result.csv");
+			return;
+		}
+
+		InputStream problemIS;
+		InputStream resultIS;
+		if (args.length == 6) {
+			// 将赛题拼成评测数据
+			StringBuffer sb = new StringBuffer();
+			for (int i = 0; i < 5; i++) {
+				List<String> lines = new ArrayList<String>();
+				BufferedReader bs = new BufferedReader(new FileReader(new File(args[i])));
+				for (String line = bs.readLine(); line != null; line = bs.readLine())
+					lines.add(line);
+				bs.close();
+				sb.append("" + lines.size());
+				for (String line : lines)
+					sb.append("\n").append(line);
+				if (i != 4)
+					sb.append("\n");
+			}
+			String alldata = sb.toString();
+			problemIS = new ByteArrayInputStream(alldata.getBytes());
+			resultIS = new FileInputStream(args[5]);
+		} else {
+			problemIS = new FileInputStream(args[0]);
+			resultIS = new FileInputStream(args[1]);
+		}
+
+		// 评测
+		AlibabaSchedulerEvaluatorRun evaluator = new AlibabaSchedulerEvaluatorRun();
+		evaluator.init(new BufferedReader(new InputStreamReader(problemIS, Charsets.UTF_8)));
+		double score = evaluator.evaluate(new BufferedReader(new InputStreamReader(resultIS, Charsets.UTF_8)));
+		System.out.println("选手所得分数为:" + score);
+	}
+
+	// 评测问题
+	protected double evaluate(BufferedReader bufferedReader) throws IOException {
+		double costs = 0.;
+		for (int iter = 0; iter < problems.size(); iter++) {
+			System.out.print("正在评测第" + iter + "份数据, 得分:");
+			double cost = problems.get(iter).evaluate(bufferedReader);
+			costs += cost;
+			System.out.println("" + cost);
+		}
+		return costs / problems.size();
+	}
+
+	// 读取数据
+	protected void init(BufferedReader bufferedReader) throws IOException {
+		problems = new ArrayList<Problem>();
+		while (true) {
+			String firstLine = bufferedReader.readLine();
+			if (firstLine == null)
+				break;
+			System.out.print("正在读取第" + problems.size() + "份数据");
+			Problem prob = new Problem();
+			prob.init(firstLine, bufferedReader);
+			problems.add(prob);
+			System.out.println(",成功");
+		}
+	}
+
+	class Problem {
+		// 静态数据
+		private int n; // app数
+		private int N; // inst数
+		private int m; // machine数
+		private int k = 2 * T2 + 4; // 资源种类
+		private Map<String, Integer> appIndex;
+		private Map<String, Integer> machineIndex;
+		private String[] apps;
+		private String[] machines;
+		private Map<String, Integer> inst2AppIndex;
+		private double[][] appResources;
+		private double[][] machineResources;
+		private Map<Integer, Integer>[] appInterference;
+
+		private String[] jobs;
+		private int jobN;
+		private Map<String, Integer> jobIndex;
+		private double[][] jobResources;
+		private int[] jobR;
+		private int[] jobV;
+		private Set<Integer>[] jobFa;
+
+		// 动态数据
+		private Map<String, Integer> inst2Machine;
+		private double[][] machineResourcesUsed;
+		private Map<Integer, Integer>[] machineHasApp;
+
+		public void init(String firstLine, BufferedReader bufferedReader) throws IOException {
+			/** Read app_resources */
+			n = Integer.parseInt(firstLine);// 9338
+			apps = new String[n];
+			appIndex = new HashMap<String, Integer>();
+			appResources = new double[n][k];
+			for (int i = 0; i < n; i++) {
+				// appId,resources
+				String line = bufferedReader.readLine();
+				String[] parts = line.split(",", -1);
+				List<Double> resources = new ArrayList<Double>();
+				for (String x : parts[1].split("\\|", -1)) {
+					double cpu = Double.parseDouble(x);
+					for (int tt = 0; tt < 15; tt++)
+						resources.add(cpu);
+				}
+				for (String x : parts[2].split("\\|", -1)) {
+					double mem = Double.parseDouble(x);
+					for (int tt = 0; tt < 15; tt++)
+						resources.add(mem);
+				}
+				for (int j = 3; j < parts.length; j++)
+					resources.add(Double.parseDouble(parts[j]));
+				if (k != resources.size())
+					throw new IOException("[DEBUG 2]Invaild problem: " + k + ", " + resources.size());
+				if (appIndex.containsKey(parts[0]))
+					throw new IOException("[DEBUG 3]Invaild problem");
+				appIndex.put(parts[0], i);
+				apps[i] = parts[0];
+				for (int j = 0; j < k; j++)
+					appResources[i][j] = resources.get(j);
+			}
+			/** Read machine_resources */
+			m = Integer.parseInt(bufferedReader.readLine());// 8000
+			machineResources = new double[m][k];
+			machineResourcesUsed = new double[m][k];
+			machineIndex = new HashMap<String, Integer>();
+			machineHasApp = new Map[m];
+			machines = new String[m];
+			for (int i = 0; i < m; i++) {
+				// machineId,resources
+				String line = bufferedReader.readLine();
+				String[] parts = line.split(",", -1);
+				if (machineIndex.containsKey(parts[0]))
+					throw new IOException("[DEBUG 4]Invaild problem");
+				machineIndex.put(parts[0], i);
+				machines[i] = parts[0];
+				machineHasApp[i] = new HashMap<Integer, Integer>();
+				double cpu = Double.parseDouble(parts[1]);
+				double mem = Double.parseDouble(parts[2]);
+				for (int j = 0; j < T2; j++) {
+					machineResources[i][j] = cpu;
+					machineResources[i][T2 + j] = mem;
+				}
+				for (int j = 3; j < parts.length; j++)
+					machineResources[i][2 * T2 + j - 3] = Double.parseDouble(parts[j]);
+				for (int j = 0; j < k; j++)
+					machineResourcesUsed[i][j] = 0.;
+			}
+			/** Read instance_deploy */
+			N = Integer.parseInt(bufferedReader.readLine());
+			inst2AppIndex = new HashMap<String, Integer>();
+			inst2Machine = new HashMap<String, Integer>();
+			for (int i = 0; i < N; i++) {
+				String line = bufferedReader.readLine();
+				String[] parts = line.split(",", -1);
+				if (inst2AppIndex.containsKey(parts[0]))
+					throw new IOException("[DEBUG 5]Invaild problem");
+				if (!appIndex.containsKey(parts[1]))
+					throw new IOException("[DEBUG 6]Invaild problem");
+				inst2AppIndex.put(parts[0], appIndex.get(parts[1]));
+				if (!"".equals(parts[2])) {
+					if (!machineIndex.containsKey(parts[2]))
+						throw new IOException("[DEBUG 7]Invaild problem");
+					toMachine(parts[0], machineIndex.get(parts[2]), false);
+				}
+			}
+			/** Read app_interference */
+			int icnt = Integer.parseInt(bufferedReader.readLine());
+			appInterference = new Map[n];
+			for (int i = 0; i < n; i++)
+				appInterference[i] = new HashMap<Integer, Integer>();
+			for (int i = 0; i < icnt; i++) {
+				String line = bufferedReader.readLine();
+				String[] parts = line.split(",", -1);
+				if (!appIndex.containsKey(parts[0]) || !appIndex.containsKey(parts[1]))
+					throw new IOException("[DEBUG 8]Invaild problem");
+				int app1 = appIndex.get(parts[0]);
+				int app2 = appIndex.get(parts[1]);
+				int limit = Integer.parseInt(parts[2]);
+				Map<Integer, Integer> inter = appInterference[app1];
+				if (inter.containsKey(app2))
+					throw new IOException("[DEBUG 9]Invaild problem");
+				if (app1 == app2)
+					limit += 1; // self-interference +1 here
+				inter.put(app2, limit);
+			}
+			/** Read job_info */
+			jobN = Integer.parseInt(bufferedReader.readLine());
+			jobs = new String[jobN];
+			jobIndex = new HashMap<String, Integer>();
+			jobResources = new double[jobN][2];
+			jobV = new int[jobN];
+			jobR = new int[jobN];
+			jobFa = new Set[jobN];
+			Set<String>[] tFa = new Set[jobN];
+			for (int i = 0; i < jobN; i++) {
+				tFa[i] = new HashSet<String>();
+				String line = bufferedReader.readLine();
+				String[] parts = line.split(",", -1);
+				if (jobIndex.containsKey(parts[0]))
+					throw new IOException("[DEBUG 10]Invaild problem");
+				jobIndex.put(parts[0], i);
+				jobs[i] = parts[0];
+				jobResources[i][0] = Double.parseDouble(parts[1]);
+				jobResources[i][1] = Double.parseDouble(parts[2]);
+				jobV[i] = Integer.parseInt(parts[3]);
+				jobR[i] = Integer.parseInt(parts[4]);
+				if (jobV[i] <= 0 || jobR[i] <= 0)
+					throw new IOException("[DEBUG 10.1]Invaild problem");
+				for (int it = 5; it < parts.length; it++)
+					tFa[i].add(parts[it]);
+			}
+			for (int i = 0; i < jobN; i++) {
+				jobFa[i] = new HashSet<Integer>();
+				for (String job : tFa[i]) {
+					if (!jobIndex.containsKey(job))
+						continue;
+					jobFa[i].add(jobIndex.get(job));
+				}
+			}
+		}
+
+		public double evaluate(BufferedReader bufferedReader) throws IOException {
+			double costs = 0.;
+			try {
+				/** 读取执行数据 */
+				List<Pair<String, Integer>>[] execs = new List[EXEC_LIMIT + 1];
+				for (int i = 1; i <= EXEC_LIMIT; i++)//3
+					execs[i] = new ArrayList<Pair<String, Integer>>();
+				List<Pair<Integer, Integer>> jobExec = new ArrayList<Pair<Integer, Integer>>();
+				List<Pair<Integer, Integer>> jobExecDetail = new ArrayList<Pair<Integer, Integer>>();
+				boolean readFail = false;
+				boolean jobExecStart = false;
+				for (String line = bufferedReader.readLine(); line != null
+						&& !line.equals("#"); line = bufferedReader.readLine()) {
+					String[] pair = line.split(",", -1);
+					if ((pair.length != 3 && pair.length != 4) || (pair.length == 3 && jobExecStart)) {
+						System.out.println("Read failed: " + line);
+						readFail = true;
+						continue;
+					}
+					if (pair.length == 3) {
+						int stage = Integer.parseInt(pair[0]);
+						if (stage < 1 || stage > EXEC_LIMIT || !inst2AppIndex.containsKey(pair[1])
+								|| !machineIndex.containsKey(pair[2])) {
+							System.out.println("" + (stage < 1) + ", " + (stage > EXEC_LIMIT) + ", "
+									+ (!inst2AppIndex.containsKey(pair[1])) + ", "
+									+ (!machineIndex.containsKey(pair[2])));
+							readFail = true;
+							continue;
+						}
+						execs[stage].add(new ImmutablePair(pair[1], machineIndex.get(pair[2])));
+					} else {
+						jobExecStart = true;
+						if (!jobIndex.containsKey(pair[0]) || !machineIndex.containsKey(pair[1])) {
+							System.out.println("" + (!jobIndex.containsKey(pair[0])) + ", "
+									+ (!machineIndex.containsKey(pair[1])));
+							readFail = true;
+							continue;
+						}
+						jobExec.add(new ImmutablePair(jobIndex.get(pair[0]), machineIndex.get(pair[1])));
+						jobExecDetail.add(new ImmutablePair(Integer.parseInt(pair[2]), Integer.parseInt(pair[3])));
+					}
+				}
+				if (readFail)
+					throw new Exception("Invaild solution file");
+				/** app exec */
+				for (int stage = 1; stage <= EXEC_LIMIT; stage++) {
+					/** 记录pickFrom */
+					Map<String, Integer> pickFrom = new HashMap<String, Integer>();
+					for (Pair<String, Integer> exec : execs[stage]) {
+						String inst = exec.getLeft();
+						if (!inst2Machine.containsKey(inst))
+							continue;
+						int fromMachine = inst2Machine.get(inst);
+						if (pickFrom.containsKey(inst))
+							throw new Exception("duplicate instance: " + inst);
+						pickFrom.put(inst, fromMachine);
+					}
+					/** 执行 */
+					for (Pair<String, Integer> exec : execs[stage]) {
+						String inst = exec.getLeft();
+						Integer machineIt = exec.getRight();
+						String msg = toMachine(inst, machineIt);
+						if (!msg.equals("success"))
+							throw new Exception("执行失败, inst: " + inst + ", stage: " + stage + ", msg: " + msg);
+					}
+					/** 清理 */
+					for (String inst : pickFrom.keySet()) {
+						int machineIt = pickFrom.get(inst);
+						pickInstance(inst, machineIt);
+					}
+				}
+				/** job exec */
+				int[] jobAsigned = new int[jobN];
+				int[] jobFirstStart = new int[jobN];
+				int[] jobLastEnd = new int[jobN];
+				Set<Integer> hasJob = new HashSet<Integer>();
+				for (int i = 0; i < jobN; i++) {
+					jobAsigned[i] = 0;
+					jobFirstStart[i] = T2;
+					jobLastEnd[i] = -1;
+				}
+				for (int it = 0; it < jobExec.size(); it++) {
+					int job = jobExec.get(it).getLeft();
+					int machineIt = jobExec.get(it).getRight();
+					int start = jobExecDetail.get(it).getLeft();
+					int cnt = jobExecDetail.get(it).getRight();
+					int end = start + jobR[job] - 1;
+					if (end >= T2 || start < 0)
+						throw new Exception("job TLE, job:" + jobs[job] + ", start:" + start);
+					if (cnt <= 0)
+						throw new Exception("job assignment <= 0");
+					hasJob.add(machineIt);
+					jobAsigned[job] += cnt;
+					jobFirstStart[job] = Math.min(jobFirstStart[job], start);
+					jobLastEnd[job] = Math.max(jobLastEnd[job], end);
+					for (int i = start; i <= end; i++) {
+						machineResourcesUsed[machineIt][i] += jobResources[job][0];
+						machineResourcesUsed[machineIt][T2 + i] += jobResources[job][1];
+					}
+				}
+				/** 计算终态得分 */
+				// 检查inst是否全部放入machine
+				for (String inst : inst2AppIndex.keySet())
+					if (!inst2Machine.containsKey(inst))
+						throw new Exception("instance未全部分配, " + inst);
+				// 检查job是否全部放入machine
+				for (int job = 0; job < jobN; job++)
+					if (jobAsigned[job] != jobV[job])
+						throw new Exception("job未全部分配, " + jobs[job]);
+				// 检查job是否满足DAG
+				for (int job = 0; job < jobN; job++)
+					for (Integer fa : jobFa[job])
+						if (jobFirstStart[job] <= jobLastEnd[fa])
+							throw new Exception("DAG broken: " + jobs[fa] + ":" + jobLastEnd[fa] + " -> " + jobs[job]
+									+ ":" + jobFirstStart[job]);
+				// 检查machine的终态
+				for (int j = 0; j < m; j++) {
+					Map<Integer, Integer> hasApp = machineHasApp[j];
+					if (hasApp.size() == 0 && !hasJob.contains(j))
+						continue;
+					// 检查互斥条件
+					int appCnt = 0;
+					for (Integer conditionalApp : hasApp.keySet()) {
+						if (hasApp.get(conditionalApp) <= 0)
+							throw new Exception("[DEBUG 1]Stupid Judger.");
+						appCnt += hasApp.get(conditionalApp);
+						for (Integer checkApp : appInterference[conditionalApp].keySet()) {
+							if (hasApp.containsKey(checkApp)) {
+								if (hasApp.get(checkApp) > appInterference[conditionalApp].get(checkApp))
+									throw new Exception("终态存在干扰冲突");
+							}
+						}
+					}
+					// 检查资源限制
+					for (int i = 0; i < k; i++)
+						if (dcmp(machineResourcesUsed[j][i] - machineResources[j][i]) > 0)
+							throw new Exception("终态存在资源过载");
+					// 技术得分
+					for (int t = 0; t < T2; t++) {
+						double usage = machineResourcesUsed[j][t] / machineResources[j][t];
+						costs += 1. + (1. + appCnt) * (Math.exp(Math.max(0., usage - beta)) - 1.);
+					}
+				}
+				costs /= T2;
+			} catch (Exception e) {
+				System.out.println(e.getMessage());
+				// e.printStackTrace();
+				costs = 1e9;
+			}
+
+			return costs;
+		}
+
+		private String toMachine(String inst, int machineIt) {
+			return toMachine(inst, machineIt, true);
+		}
+
+		private String toMachine(String inst, int machineIt, boolean doCheck) {
+			int appIt = inst2AppIndex.get(inst);
+			Map<Integer, Integer> hasApp = machineHasApp[machineIt];
+			if (doCheck) {
+				// 检查互斥规则
+				int nowHas = 0;
+				if (hasApp.containsKey(appIt))
+					nowHas = hasApp.get(appIt);
+				for (Integer conditionalApp : hasApp.keySet()) {
+					if (hasApp.get(conditionalApp) <= 0)
+						continue;
+					if (!appInterference[conditionalApp].containsKey(appIt))
+						continue;
+					if (nowHas + 1 > appInterference[conditionalApp].get(appIt)) {
+						return "App Interference, inst: " + inst + ", " + apps[conditionalApp] + " -> " + apps[appIt]
+								+ ", " + (nowHas + 1) + " > " + appInterference[conditionalApp].get(appIt);
+					}
+				}
+				for (Integer checkApp : hasApp.keySet()) {
+					if (!appInterference[appIt].containsKey(checkApp))
+						continue;
+					if (hasApp.get(checkApp) > appInterference[appIt].get(checkApp)) {
+						return "App Interference, inst: " + inst + ", " + apps[appIt] + " -> " + apps[checkApp] + ", "
+								+ (nowHas + 1) + " > " + appInterference[appIt].get(checkApp);
+					}
+				}
+				// 检查资源限制
+				for (int i = 0; i < k; i++)
+					if (dcmp(machineResourcesUsed[machineIt][i] + appResources[appIt][i]
+							- machineResources[machineIt][i]) > 0)
+						return "Resource Limit: inst: " + inst + ", " + "machine: " + machines[machineIt] + ", app: "
+								+ apps[appIt] + ", resIter: " + i + ", " + machineResourcesUsed[machineIt][i] + " + "
+								+ appResources[appIt][i] + " > " + machineResources[machineIt][i];
+			}
+			// 将inst放入新的machine
+			inst2Machine.put(inst, machineIt);
+			if (!hasApp.containsKey(appIt))
+				hasApp.put(appIt, 0);
+			hasApp.put(appIt, hasApp.get(appIt) + 1);
+			for (int i = 0; i < k; i++)
+				machineResourcesUsed[machineIt][i] += appResources[appIt][i];
+
+			return "success";
+		}
+
+		private void pickInstance(String inst, int fromMachine) throws Exception {
+			int appIt = inst2AppIndex.get(inst);
+			// 更新machineHasApp
+			Map<Integer, Integer> fromHasApp = machineHasApp[fromMachine];
+			if (!fromHasApp.containsKey(appIt))
+				throw new Exception("[DEBUG 12] Stupid judger");
+			fromHasApp.put(appIt, fromHasApp.get(appIt) - 1);
+			if (fromHasApp.get(appIt) <= 0)
+				fromHasApp.remove(appIt);
+			// 更新machineResourcesUsed
+			for (int i = 0; i < k; i++)
+				machineResourcesUsed[fromMachine][i] -= appResources[appIt][i];
+		}
 
-    private int dcmp(double x) {
-        if (Math.abs(x) < 1e-9) return 0;
-        return x < 0. ? -1 : 1;
-    }
-    
-    public static void main(String[] args) throws Exception {
-        if (args.length != 5 && args.length != 2){
-            System.err.println("传入参数有误,使用方式为:java -cp xxx.jar com.aliyun.tianchi.mgr.evaluate.evaluate.file.evaluator.AlibabaSchedulerEvaluatorRun app_resources.csv machine_resources.csv instance_deploy.csv app_interference.csv result.csv");
-            return;
-        }
-        
-        InputStream problem;
-        InputStream result;
-        if (args.length == 5) {
-            // 将赛题拼成评测数据
-            StringBuffer sb = new StringBuffer();
-            for (int i = 0; i < 4; i++) {
-                List<String> lines = new ArrayList<String>();
-                BufferedReader bs = new BufferedReader(new FileReader(new File(args[i])));
-                for (String line = bs.readLine(); line != null; line = bs.readLine())
-                    lines.add(line);
-                sb.append(""+lines.size()).append("\n");
-                for (String line : lines)
-                    sb.append(line).append("\n");
-            }
-            String alldata = sb.toString();
-            problem = new ByteArrayInputStream(alldata.getBytes());
-            result = new FileInputStream(args[4]);
-        }
-        else {
-            problem = new FileInputStream(args[0]);
-            result = new FileInputStream(args[1]);
-        }
-        
-        // 评测
-        AlibabaSchedulerEvaluatorRun evaluator = new AlibabaSchedulerEvaluatorRun();
-        evaluator.init(new BufferedReader(new InputStreamReader(problem, Charsets.UTF_8)));
-        double score = evaluator.evaluate(new BufferedReader(new InputStreamReader(result, Charsets.UTF_8)));
-        System.out.println("选手所得分数为:" + score);
-    }
+		private int dcmp(double x) {
+			if (Math.abs(x) < 1e-9)
+				return 0;
+			return x < 0. ? -1 : 1;
+		}
+	}
 }

+ 451 - 0
java/me/yoqi/servermanager/Main4.java

@@ -0,0 +1,451 @@
+package me.yoqi.servermanager;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+
+import com.aliyun.tianchi.mgr.evaluate.evaluate.file.evaluator.AlibabaSchedulerEvaluatorRun;
+import com.google.common.base.Charsets;
+
+public class Main4 {
+	// 参数
+	public static final double beta = 0.5;
+	public static final int T2 = 98 * 15;
+	public static final int EXEC_LIMIT = 3;
+
+	private List<Problem> problems;
+
+	public static void main(String[] args) throws IOException {
+		InputStream problemIS;
+		// 将赛题拼成评测数据
+		StringBuffer sb = new StringBuffer();
+		for (int i = 0; i < 5; i++) {
+			List<String> lines = new ArrayList<String>();
+			BufferedReader bs = new BufferedReader(new FileReader(new File(args[i])));
+			for (String line = bs.readLine(); line != null; line = bs.readLine())
+				lines.add(line);
+			bs.close();
+			sb.append("" + lines.size());
+			for (String line : lines)
+				sb.append("\n").append(line);
+			if (i != 4)
+				sb.append("\n");
+		}
+		String alldata = sb.toString();
+		problemIS = new ByteArrayInputStream(alldata.getBytes());
+
+		// 评测
+		Main4 evaluator = new Main4();
+		evaluator.init(new BufferedReader(new InputStreamReader(problemIS, Charsets.UTF_8)));
+		List<Problem> problems=new ArrayList<Problem>();
+		problems.get(0).run(null);
+	}
+	
+	// 读取数据
+	protected void init(BufferedReader bufferedReader) throws IOException {
+		String firstLine = bufferedReader.readLine();
+		Problem prob = new Problem();
+		prob.init(firstLine, bufferedReader);
+		problems.add(prob);
+		System.out.println("init 成功");
+	}
+
+	class Problem {
+		// 静态数据
+		private int n; // app数
+		private int N; // inst数
+		private int m; // machine数
+		private int k = 2 * T2 + 4; // 资源种类
+		private Map<String, Integer> appIndex;
+		private Map<String, Integer> machineIndex;
+		private String[] apps;
+		private String[] machines;
+		private Map<String, Integer> inst2AppIndex;
+		private double[][] appResources;
+		private double[][] machineResources;
+		private Map<Integer, Integer>[] appInterference;
+
+		private String[] jobs;
+		private int jobN;
+		private Map<String, Integer> jobIndex;
+		private double[][] jobResources;
+		private int[] jobR;
+		private int[] jobV;
+		private Set<Integer>[] jobFa;
+
+		// 动态数据
+		private Map<String, Integer> inst2Machine;
+		private double[][] machineResourcesUsed;
+		private Map<Integer, Integer>[] machineHasApp;
+
+		public void init(String firstLine, BufferedReader bufferedReader) throws IOException {
+			/** Read app_resources */
+			n = Integer.parseInt(firstLine);// 9338
+			apps = new String[n];
+			appIndex = new HashMap<String, Integer>();
+			appResources = new double[n][k];
+			for (int i = 0; i < n; i++) {
+				// appId,resources
+				String line = bufferedReader.readLine();
+				String[] parts = line.split(",", -1);
+				List<Double> resources = new ArrayList<Double>();
+				for (String x : parts[1].split("\\|", -1)) {
+					double cpu = Double.parseDouble(x);
+					for (int tt = 0; tt < 15; tt++)
+						resources.add(cpu);
+				}
+				for (String x : parts[2].split("\\|", -1)) {
+					double mem = Double.parseDouble(x);
+					for (int tt = 0; tt < 15; tt++)
+						resources.add(mem);
+				}
+				for (int j = 3; j < parts.length; j++)
+					resources.add(Double.parseDouble(parts[j]));
+				if (k != resources.size())
+					throw new IOException("[DEBUG 2]Invaild problem: " + k + ", " + resources.size());
+				if (appIndex.containsKey(parts[0]))
+					throw new IOException("[DEBUG 3]Invaild problem");
+				appIndex.put(parts[0], i);
+				apps[i] = parts[0];
+				for (int j = 0; j < k; j++)
+					appResources[i][j] = resources.get(j);
+			}
+			/** Read machine_resources */
+			m = Integer.parseInt(bufferedReader.readLine());// 8000
+			machineResources = new double[m][k];
+			machineResourcesUsed = new double[m][k];
+			machineIndex = new HashMap<String, Integer>();
+			machineHasApp = new Map[m];
+			machines = new String[m];
+			for (int i = 0; i < m; i++) {
+				// machineId,resources
+				String line = bufferedReader.readLine();
+				String[] parts = line.split(",", -1);
+				if (machineIndex.containsKey(parts[0]))
+					throw new IOException("[DEBUG 4]Invaild problem");
+				machineIndex.put(parts[0], i);
+				machines[i] = parts[0];
+				machineHasApp[i] = new HashMap<Integer, Integer>();
+				double cpu = Double.parseDouble(parts[1]);
+				double mem = Double.parseDouble(parts[2]);
+				for (int j = 0; j < T2; j++) {
+					machineResources[i][j] = cpu;
+					machineResources[i][T2 + j] = mem;
+				}
+				for (int j = 3; j < parts.length; j++)
+					machineResources[i][2 * T2 + j - 3] = Double.parseDouble(parts[j]);
+				for (int j = 0; j < k; j++)
+					machineResourcesUsed[i][j] = 0.;
+			}
+			/** Read instance_deploy */
+			N = Integer.parseInt(bufferedReader.readLine());
+			inst2AppIndex = new HashMap<String, Integer>();
+			inst2Machine = new HashMap<String, Integer>();
+			for (int i = 0; i < N; i++) {
+				String line = bufferedReader.readLine();
+				String[] parts = line.split(",", -1);
+				if (inst2AppIndex.containsKey(parts[0]))
+					throw new IOException("[DEBUG 5]Invaild problem");
+				if (!appIndex.containsKey(parts[1]))
+					throw new IOException("[DEBUG 6]Invaild problem");
+				inst2AppIndex.put(parts[0], appIndex.get(parts[1]));
+				if (!"".equals(parts[2])) {
+					if (!machineIndex.containsKey(parts[2]))
+						throw new IOException("[DEBUG 7]Invaild problem");
+					toMachine(parts[0], machineIndex.get(parts[2]), false);
+				}
+			}
+			/** Read app_interference */
+			int icnt = Integer.parseInt(bufferedReader.readLine());
+			appInterference = new Map[n];
+			for (int i = 0; i < n; i++)
+				appInterference[i] = new HashMap<Integer, Integer>();
+			for (int i = 0; i < icnt; i++) {
+				String line = bufferedReader.readLine();
+				String[] parts = line.split(",", -1);
+				if (!appIndex.containsKey(parts[0]) || !appIndex.containsKey(parts[1]))
+					throw new IOException("[DEBUG 8]Invaild problem");
+				int app1 = appIndex.get(parts[0]);
+				int app2 = appIndex.get(parts[1]);
+				int limit = Integer.parseInt(parts[2]);
+				Map<Integer, Integer> inter = appInterference[app1];
+				if (inter.containsKey(app2))
+					throw new IOException("[DEBUG 9]Invaild problem");
+				if (app1 == app2)
+					limit += 1; // self-interference +1 here
+				inter.put(app2, limit);
+			}
+			/** Read job_info */
+			jobN = Integer.parseInt(bufferedReader.readLine());
+			jobs = new String[jobN];
+			jobIndex = new HashMap<String, Integer>();
+			jobResources = new double[jobN][2];
+			jobV = new int[jobN];
+			jobR = new int[jobN];
+			jobFa = new Set[jobN];
+			Set<String>[] tFa = new Set[jobN];
+			for (int i = 0; i < jobN; i++) {
+				tFa[i] = new HashSet<String>();
+				String line = bufferedReader.readLine();
+				String[] parts = line.split(",", -1);
+				if (jobIndex.containsKey(parts[0]))
+					throw new IOException("[DEBUG 10]Invaild problem");
+				jobIndex.put(parts[0], i);
+				jobs[i] = parts[0];
+				jobResources[i][0] = Double.parseDouble(parts[1]);
+				jobResources[i][1] = Double.parseDouble(parts[2]);
+				jobV[i] = Integer.parseInt(parts[3]);
+				jobR[i] = Integer.parseInt(parts[4]);
+				if (jobV[i] <= 0 || jobR[i] <= 0)
+					throw new IOException("[DEBUG 10.1]Invaild problem");
+				for (int it = 5; it < parts.length; it++)
+					tFa[i].add(parts[it]);
+			}
+			for (int i = 0; i < jobN; i++) {
+				jobFa[i] = new HashSet<Integer>();
+				for (String job : tFa[i]) {
+					if (!jobIndex.containsKey(job))
+						continue;
+					jobFa[i].add(jobIndex.get(job));
+				}
+			}
+		}
+
+		public double run(BufferedReader bufferedReader) throws IOException {
+			double costs = 0.;
+			try {
+				/** 读取执行数据 */
+				List<Pair<String, Integer>>[] execs = new List[EXEC_LIMIT + 1];
+				for (int i = 1; i <= EXEC_LIMIT; i++)// 3
+					execs[i] = new ArrayList<Pair<String, Integer>>();
+				List<Pair<Integer, Integer>> jobExec = new ArrayList<Pair<Integer, Integer>>();
+				List<Pair<Integer, Integer>> jobExecDetail = new ArrayList<Pair<Integer, Integer>>();
+				boolean readFail = false;
+				boolean jobExecStart = false;
+				for (String line = bufferedReader.readLine(); line != null
+						&& !line.equals("#"); line = bufferedReader.readLine()) {
+					String[] pair = line.split(",", -1);
+					if ((pair.length != 3 && pair.length != 4) || (pair.length == 3 && jobExecStart)) {
+						System.out.println("Read failed: " + line);
+						readFail = true;
+						continue;
+					}
+					if (pair.length == 3) {
+						int stage = Integer.parseInt(pair[0]);
+						if (stage < 1 || stage > EXEC_LIMIT || !inst2AppIndex.containsKey(pair[1])
+								|| !machineIndex.containsKey(pair[2])) {
+							System.out.println("" + (stage < 1) + ", " + (stage > EXEC_LIMIT) + ", "
+									+ (!inst2AppIndex.containsKey(pair[1])) + ", "
+									+ (!machineIndex.containsKey(pair[2])));
+							readFail = true;
+							continue;
+						}
+						execs[stage].add(new ImmutablePair(pair[1], machineIndex.get(pair[2])));
+					} else {
+						jobExecStart = true;
+						if (!jobIndex.containsKey(pair[0]) || !machineIndex.containsKey(pair[1])) {
+							System.out.println("" + (!jobIndex.containsKey(pair[0])) + ", "
+									+ (!machineIndex.containsKey(pair[1])));
+							readFail = true;
+							continue;
+						}
+						jobExec.add(new ImmutablePair(jobIndex.get(pair[0]), machineIndex.get(pair[1])));
+						jobExecDetail.add(new ImmutablePair(Integer.parseInt(pair[2]), Integer.parseInt(pair[3])));
+					}
+				}
+				if (readFail)
+					throw new Exception("Invaild solution file");
+				/** app exec */
+				for (int stage = 1; stage <= EXEC_LIMIT; stage++) {
+					/** 记录pickFrom */
+					Map<String, Integer> pickFrom = new HashMap<String, Integer>();
+					for (Pair<String, Integer> exec : execs[stage]) {
+						String inst = exec.getLeft();
+						if (!inst2Machine.containsKey(inst))
+							continue;
+						int fromMachine = inst2Machine.get(inst);
+						if (pickFrom.containsKey(inst))
+							throw new Exception("duplicate instance: " + inst);
+						pickFrom.put(inst, fromMachine);
+					}
+					/** 执行 */
+					for (Pair<String, Integer> exec : execs[stage]) {
+						String inst = exec.getLeft();
+						Integer machineIt = exec.getRight();
+						String msg = toMachine(inst, machineIt);
+						if (!msg.equals("success"))
+							throw new Exception("执行失败, inst: " + inst + ", stage: " + stage + ", msg: " + msg);
+					}
+					/** 清理 */
+					for (String inst : pickFrom.keySet()) {
+						int machineIt = pickFrom.get(inst);
+						pickInstance(inst, machineIt);
+					}
+				}
+				/** job exec */
+				int[] jobAsigned = new int[jobN];
+				int[] jobFirstStart = new int[jobN];
+				int[] jobLastEnd = new int[jobN];
+				Set<Integer> hasJob = new HashSet<Integer>();
+				for (int i = 0; i < jobN; i++) {
+					jobAsigned[i] = 0;
+					jobFirstStart[i] = T2;
+					jobLastEnd[i] = -1;
+				}
+				for (int it = 0; it < jobExec.size(); it++) {
+					int job = jobExec.get(it).getLeft();
+					int machineIt = jobExec.get(it).getRight();
+					int start = jobExecDetail.get(it).getLeft();
+					int cnt = jobExecDetail.get(it).getRight();
+					int end = start + jobR[job] - 1;
+					if (end >= T2 || start < 0)
+						throw new Exception("job TLE, job:" + jobs[job] + ", start:" + start);
+					if (cnt <= 0)
+						throw new Exception("job assignment <= 0");
+					hasJob.add(machineIt);
+					jobAsigned[job] += cnt;
+					jobFirstStart[job] = Math.min(jobFirstStart[job], start);
+					jobLastEnd[job] = Math.max(jobLastEnd[job], end);
+					for (int i = start; i <= end; i++) {
+						machineResourcesUsed[machineIt][i] += jobResources[job][0];
+						machineResourcesUsed[machineIt][T2 + i] += jobResources[job][1];
+					}
+				}
+				/** 计算终态得分 */
+				// 检查inst是否全部放入machine
+				for (String inst : inst2AppIndex.keySet())
+					if (!inst2Machine.containsKey(inst))
+						throw new Exception("instance未全部分配, " + inst);
+				// 检查job是否全部放入machine
+				for (int job = 0; job < jobN; job++)
+					if (jobAsigned[job] != jobV[job])
+						throw new Exception("job未全部分配, " + jobs[job]);
+				// 检查job是否满足DAG
+				for (int job = 0; job < jobN; job++)
+					for (Integer fa : jobFa[job])
+						if (jobFirstStart[job] <= jobLastEnd[fa])
+							throw new Exception("DAG broken: " + jobs[fa] + ":" + jobLastEnd[fa] + " -> " + jobs[job]
+									+ ":" + jobFirstStart[job]);
+				// 检查machine的终态
+				for (int j = 0; j < m; j++) {
+					Map<Integer, Integer> hasApp = machineHasApp[j];
+					if (hasApp.size() == 0 && !hasJob.contains(j))
+						continue;
+					// 检查互斥条件
+					int appCnt = 0;
+					for (Integer conditionalApp : hasApp.keySet()) {
+						if (hasApp.get(conditionalApp) <= 0)
+							throw new Exception("[DEBUG 1]Stupid Judger.");
+						appCnt += hasApp.get(conditionalApp);
+						for (Integer checkApp : appInterference[conditionalApp].keySet()) {
+							if (hasApp.containsKey(checkApp)) {
+								if (hasApp.get(checkApp) > appInterference[conditionalApp].get(checkApp))
+									throw new Exception("终态存在干扰冲突");
+							}
+						}
+					}
+					// 检查资源限制
+					for (int i = 0; i < k; i++)
+						if (dcmp(machineResourcesUsed[j][i] - machineResources[j][i]) > 0)
+							throw new Exception("终态存在资源过载");
+					// 技术得分
+					for (int t = 0; t < T2; t++) {
+						double usage = machineResourcesUsed[j][t] / machineResources[j][t];
+						costs += 1. + (1. + appCnt) * (Math.exp(Math.max(0., usage - beta)) - 1.);
+					}
+				}
+				costs /= T2;
+			} catch (Exception e) {
+				System.out.println(e.getMessage());
+				// e.printStackTrace();
+				costs = 1e9;
+			}
+
+			return costs;
+		}
+
+		private String toMachine(String inst, int machineIt) {
+			return toMachine(inst, machineIt, true);
+		}
+
+		private String toMachine(String inst, int machineIt, boolean doCheck) {
+			int appIt = inst2AppIndex.get(inst);
+			Map<Integer, Integer> hasApp = machineHasApp[machineIt];
+			if (doCheck) {
+				// 检查互斥规则
+				int nowHas = 0;
+				if (hasApp.containsKey(appIt))
+					nowHas = hasApp.get(appIt);
+				for (Integer conditionalApp : hasApp.keySet()) {
+					if (hasApp.get(conditionalApp) <= 0)
+						continue;
+					if (!appInterference[conditionalApp].containsKey(appIt))
+						continue;
+					if (nowHas + 1 > appInterference[conditionalApp].get(appIt)) {
+						return "App Interference, inst: " + inst + ", " + apps[conditionalApp] + " -> " + apps[appIt]
+								+ ", " + (nowHas + 1) + " > " + appInterference[conditionalApp].get(appIt);
+					}
+				}
+				for (Integer checkApp : hasApp.keySet()) {
+					if (!appInterference[appIt].containsKey(checkApp))
+						continue;
+					if (hasApp.get(checkApp) > appInterference[appIt].get(checkApp)) {
+						return "App Interference, inst: " + inst + ", " + apps[appIt] + " -> " + apps[checkApp] + ", "
+								+ (nowHas + 1) + " > " + appInterference[appIt].get(checkApp);
+					}
+				}
+				// 检查资源限制
+				for (int i = 0; i < k; i++)
+					if (dcmp(machineResourcesUsed[machineIt][i] + appResources[appIt][i]
+							- machineResources[machineIt][i]) > 0)
+						return "Resource Limit: inst: " + inst + ", " + "machine: " + machines[machineIt] + ", app: "
+								+ apps[appIt] + ", resIter: " + i + ", " + machineResourcesUsed[machineIt][i] + " + "
+								+ appResources[appIt][i] + " > " + machineResources[machineIt][i];
+			}
+			// 将inst放入新的machine
+			inst2Machine.put(inst, machineIt);
+			if (!hasApp.containsKey(appIt))
+				hasApp.put(appIt, 0);
+			hasApp.put(appIt, hasApp.get(appIt) + 1);
+			for (int i = 0; i < k; i++)
+				machineResourcesUsed[machineIt][i] += appResources[appIt][i];
+
+			return "success";
+		}
+
+		private void pickInstance(String inst, int fromMachine) throws Exception {
+			int appIt = inst2AppIndex.get(inst);
+			// 更新machineHasApp
+			Map<Integer, Integer> fromHasApp = machineHasApp[fromMachine];
+			if (!fromHasApp.containsKey(appIt))
+				throw new Exception("[DEBUG 12] Stupid judger");
+			fromHasApp.put(appIt, fromHasApp.get(appIt) - 1);
+			if (fromHasApp.get(appIt) <= 0)
+				fromHasApp.remove(appIt);
+			// 更新machineResourcesUsed
+			for (int i = 0; i < k; i++)
+				machineResourcesUsed[fromMachine][i] -= appResources[appIt][i];
+		}
+
+		private int dcmp(double x) {
+			if (Math.abs(x) < 1e-9)
+				return 0;
+			return x < 0. ? -1 : 1;
+		}
+	}
+
+}