战舰日常任务hihocoder --优先级队列使用技巧

描述

小Hi最近在玩一款游戏。他在游戏中一共建造了N艘战舰。这N艘战舰分属6种不同的类别:航空母舰(CV)、驱逐舰(DD)、轻型巡洋舰(CL)、重型巡洋舰(CA)、战列舰(BB)和战列巡洋舰(BC)。此外每艘战舰还有两个属性,一个是战舰等级另一个是战舰的战斗力。

每天系统都会委派M件日常任务给小Hi。每件任务都对执行该任务的战舰种类、数量和等级有要求。具体来说我们可以用7个数字(A, B, C, D, E, F, G)来描述一件任务的要求:执行该任务需要A艘CV、B艘DD、C艘CL、D艘CA、E艘BB、F艘BC并且所有勇士等级都不低于G。

小Hi希望他除了派出战舰执行M件日常任务之外,还能保存尽量高的战斗力去推进主线任务。换句话说,他希望执行M件日常任务的战舰的总战斗力尽量低。

小Hi想知道执行M件日常任务,派出的战舰的战斗力之和最低是多少。(一艘战舰最多执行一件任务)

输入

第一行包含一个整数T,代表测试数据的组数。(1 ≤ T ≤ 10)

对于每组数据,第一行包含两个整数N和M。(1 ≤ N ≤ 100000, 1 ≤ M ≤ 10000)

以下N行每行描述一艘战舰。首先是两个大写字母表示类别,之后是两个整数L和F表示等级和战斗力。(1 ≤ L, F ≤ 10000)

再之后M行每行包含七个整数(A, B, C, D, E, F, G)描述一件任务。(0 ≤ A, B, C, D, E, F ≤ 10, 1 ≤ G ≤ 10000)

输出

对于每组数据,输出一个整数代表最低的战斗力之和。如果小Hi的战舰无法同时执行M件任务,输出-1。

样例输入

1
4 2
CV 1 100
CV 25 10
DD 1 10
DD 25 100
1 1 0 0 0 0 1
0 1 0 0 0 0 25

样例输出

120
-1

问题分析

通过阅读题意,可以简单的认为问题的本质在于将所拥有的N艘各种战舰派去执行M个不同的任务,希望派出的战舰在满足任务要求的前提下,战斗力之和是最小的。每种战舰有两个属性 等级L 和 战斗力F。设计如下的算法来解决当前的问题:

从需要战舰等级最高的任务开始,依次对几个不同的任务进行处理。对于当前要处理的任务,对需要的每种战舰,将所拥有的该类战舰中等级L >= G的战舰提取出来,再从提取出来的战舰集合中选取战斗力最低的战舰分配给该任务。通过对每个任务进行上述处理,将所需要的战舰分配给每个任务就能使派出的所有战舰的战斗力量最小化。上述解题的思路可以简单的描述为,找到满足等级需求的战舰,然后将满足等级需求的战舰中战斗力最小的战舰派出去。解题的思路是很明显的,该题的主要难度在于如何利用数据结构和算法,降低复杂度,使能通过题目设定的时间要求。

技巧点

该问题中的存在一种典型的矛盾,在一个拥有两维数据(key1, key2)的个体中,先找到这些个体中key1 >= X 的一群数据,然后要在满足这些要求的个体中,找到key2最小的一个个体。单纯的对个体进行排序或使用二叉排序树是很难满足两个操作都很快的要求,我们将个体通过以key1进行排序,能让我们很快的找到key1 >= X的个体,但是无法很快找到key2最小的个体,我们需要遍历整个key1 >= X的个体来找到我们需要的个体,对于少量的这种操作,算法勉强能达到我们的要求,但是在该问题中,这种操作是大量的,如果每一次我们都去遍历key1 >= X的个体,算法效率会很低。我们可以通过优先级队列的方法来加速对于存在多次上述两种操作的过程,具体描述如下:

如果我们要对一堆个体的执行多次的上述操作,我们将其每次操作的key1的边界先排个序,【X1, X2, X3, …..】 X1 >= X2 >= X3 >= …….。
我们先将所有的个体按照key1进行排序,得到排序好的队列LIST,再申请一个空的优先级队列PQ(PQ的比较方式采用个体的key2), 依次对【X1, X2, X3, …..】进行处理。

(1)先将从【X1,X2,X3,…..】中按顺序得到当前需要处理的X

(2)在LIST中找到key1 >= X的个体,将他们从LIST中移除,并添加到PQ优先级队列中

(3)从PQ队列中移除队头,即key2最小的个体。

(4)返回执行步骤(1)

上述的处理过程通过巧妙的将每次操作的X进行排序,使整个过程变得更为连续,每次操作的时候PQ队列中都是满足要求的个体,因为X1 >= X2 >= X3 …..,前面的操作遗留在PQ队列中的个体肯能是满足后面操作的key1条件约束,这就是整个过程的巧妙之处。有效的平衡了两个维度查找之间的矛盾,加速了整个处理过程。

import java.Util.*;

class Boat {
    public int Level = -1; //船的等级
    public int attack = -1; //船的攻击力量
    public String type = "";

    public Boat(int Level, int attack, String type) {
        this.Level = Level;
        this.attack = attack;
        this.type = type;
    }
};
//等级比较器
class LCom implements Comparator<Boat> {
    public int compare(Boat o1, Boat o2) {
        return o1.Level - o2.Level;
    }
};
//攻击力比较器
class AttackCom implements Comparator<Boat> {
    public int compare(Boat o1, Boat o2) {
        return o1.attack - o2.attack;
    }
}

class Task {
    public TreeMap<String, Integer> need_boats = new TreeMap<String, Integer>(); //船的类型,以及需要船的数量
    public int G = -1; //需要船的最低等级。
    public Task(int cv, int dd, int cl, int ca, int bb, int bc, int G) {
        this.need_boats.put("CV", cv);
        this.need_boats.put("DD", dd);
        this.need_boats.put("CL", cl);
        this.need_boats.put("CA", ca);
        this.need_boats.put("BB", bb);
        this.need_boats.put("BC", bc);
        this.G = G;
    }
};

class TaskCom implements Comparator<Task> {
    public int compare(Task o1, Task o2) {
        return o2.G - o1.G;
    }
};

//将样例的数据填充到数据结构中
//1  
//4 2  
//CV 1 100  
//CV 25 10  
//DD 1 10  
//DD 25 100  
//1 1 0 0 0 0 1  
//0 1 0 0 0 0 25  
TreeMap<String, ArrayList<Boat>> type_boats = new TreeMap<String, ArrayList<Boat>>();
type_boats.put("CV", new ArrayList<Boat>());
type_boats.put("DD", new ArrayList<Boat>());
type_boats.put("CL", new ArrayList<Boat>());
type_boats.put("CA", new ArrayList<Boat>());
type_boats.put("BB", new ArrayList<Boat>());
type_boats.put("BC", new ArrayList<Boat>());

type_boats.get("CV").add(new Boat(1, 100, "CV"));
type_boats.get("CV").add(new Boat(25, 10, "CV"));
type_boats.get("DD").add(new Boat(1, 10, "DD"));
type_boats.get("DD").add(new Boat(25, 100, "DD"));

ArrayList<Task> tasks = new ArrayList<Task>();
tasks.add(new Task(1, 1, 0, 0, 0, 0, 1));
tasks.add(new Task(0, 1, 0, 0, 0, 0, 25));

public int solve(TreeMap<String, ArrayList<Boat>> type_boats, ArrayList<Task> tasks) {
    for(ArrayList<Boat> boats : type_boats.values()) {
        Collections.sort(boats, new LCom());
    }
    Collections.sort(tasks, new TaskCom()); // 将任务G 从大到小进行排序
    String[] boat_types = new String[6];
    boat_types[0] = "CV";
    boat_types[1] = "DD";
    boat_types[2] = "CL";
    boat_types[3] = "CA";
    boat_types[4] = "BB";
    boat_types[5] = "BC";
    int ans = 0;
    for(String type : boat_types) {
        PriorityQueue<Boat> pqlist = new PriorityQueue<Boat>(new AttackCom());
        for(Task task : tasks) { // 将任务按等级G从大到小遍历
            int need_num = task.need_boats.get(type);
            if(need_num == 0) continue;
            ArrayList<Boat> boats = type_boats.get(type);
            while(!boats.isEmpty()) {
                Boat last_boat = boats.get(boats.size() - 1);
                if(last_boat.Level >= task.G) {
                    pqlist.add(last_boat);
                    boats.remove(boats.size() - 1);
                } else {
                    break;
                }
            }
            while(need_num != 0 && !pqlist.isEmpty()) {
                Boat boat = pqlist.poll();
                need_num--;
                ans += boat.attack;
            }
            if(need_num != 0) return -1;
        }
    }
    return ans;
} 

System.out.println("the ans is :" + solve(type_boats, tasks)); //打印答案
the ans is :120
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值