贪心算法【区间调度】【背包问题】【集合覆盖】【旅行商问题】【哈夫曼构造价值树】

贪心算法

 在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
 贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

1.区间调度问题

假设有如下课程,希望尽可能多的将课程安排在一间教室里:

课程开始时间结束时间
美术9AM10AM
英语9:30AM10:30AM
数学10AM11AM
计算机10:30AM11:30AM
音乐11AM12AM

算法设计:

  • 1.选择结束最早的课,便是要在这教室上课的第一节课
  • 2.接下来,选择第一堂课结束后才开始的课,并且结束最早的课,这将是第二节在教室上的课。

代码:

import java.util.*;
public class Main{
    public static void main(String[] args) {
        String subject[] = {"英语","数学","计算机","音乐","美术"};
        Work[]  works = {
                new Work(1,3),
                new Work(2, 5),
                new Work(4, 7),
                new Work(6, 9),
                new Work(8, 10)
        };
        int result = solution(works);
        System.out.println(result);
    }
    private static int solution(Work[] works) {
        //works里面已经按end从小到大排序了
        Arrays.sort(works);
        int count = 0;
        //当前工作的结束时间
        int endTime = 0;
        for(int i = 0;i<works.length;i++){
            if(endTime<works[i].getStart()){
                count++;
                endTime = works[i].getEnd();
            }
        }
        return count;
    }
    static class Work implements Comparable{
        private int start;
        private int end;
        public Work(int start, int end) {
            this.start = start;
            this.end = end;
        }
        //end 从小到大排序
        @Override
        public int compareTo(Object o) {
            Work work = (Work) o;
            if(this.end > work.getEnd()){
                return 1;
            }else if(this.end == work.getEnd()){
                return 0;
            }else{
                return -1;
            }
        }
        public int getStart() {
            return start;
        }
        public int getEnd() {
            return end;
        } 
    }
}

相同问题:2019届爱奇艺校招笔试题第三题_库特君的面条:   https://blog.csdn.net/qq_17556191/article/details/95003363

2.背包问题

见:https://blog.csdn.net/qq_17556191/article/details/94764606

3.集合覆盖问题

假设存在如下表的需要付费的广播台,以及广播台信号可以覆盖的地区。 如何选择最少的广播台,让所有的地区都可以接收到信号。

广播台覆盖地区
K1ID,NV,UT
K2WA,ID,MT
K3OR,NV,CA
K4NV,UT
K5CA,AZ

算法设计:
(1) 选出一个广播台,即它覆盖了最多未覆盖的地区即便包含一些已覆盖的地区也没关系
(2) 重复第一步直到覆盖了全部的地区

import java.util.*;
public class Main_tanxin_jihefugai {
    public static void main(String[] args) {
        HashMap<String,HashSet<String>> broadcasts = new HashMap<>();
        broadcasts.put("K1", new HashSet(Arrays.asList(new String[] {"ID","NV","UT"})));
        broadcasts.put("K2", new HashSet(Arrays.asList(new String[] {"WA","ID","MT"})));
        broadcasts.put("K3", new HashSet(Arrays.asList(new String[] {"OR","NV","CA"})));
        broadcasts.put("K4", new HashSet(Arrays.asList(new String[] {"NV","UT"})));
        broadcasts.put("K5", new HashSet(Arrays.asList(new String[] {"CA","AZ"})));
        //需要覆盖的全部地区
        HashSet<String> allAreas = new HashSet<>(Arrays.asList(new String[] {"ID","NV","UT","WA","MT","OR","CA","AZ"}));
        //所选择的广播台列表
        List<String> lists = new ArrayList<>();
        //(1) 选出一个广播台,即它覆盖了最多未覆盖的地区即便包含一些已覆盖的地区也没关系
        // (2) 重复第一步直到可以覆盖了全部的地区
        HashSet<String> tempSet = new HashSet<>();
        String maxKey = null;
        while(allAreas.size()!=0){
            maxKey = null;
            //一轮轮选出(之前未覆盖的)数量最多的电台
            for(String key:broadcasts.keySet()){
                tempSet.clear();
                HashSet<String> areas = broadcasts.get(key);
                tempSet.addAll(areas);
                //求出2个集合的交集,得到的交集的电台存在tempSet里面
                tempSet.retainAll(allAreas);
                //如果该集合包含的地区数量比原本的集合多
                if (tempSet.size()>0 && (maxKey == null || tempSet.size() > broadcasts.get(maxKey).size())) {
                    //得到要存入的电台key
                    maxKey = key;
                }

            }
            //找到未覆盖电台数的最大的那个电台
            if (maxKey != null) {
                lists.add(maxKey);
                //将覆盖地区移除allAreas
                allAreas.removeAll(broadcasts.get(maxKey));
            }
        }
        System.out.print("selects:" + lists);

    }
}

4.旅行商问题

 给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。
算法设计:
随机选一个城市开始,以寻找离该城市最近点作为下一个城市旅行城市,此后寻找离最后加入路线的城市最近的城市,直到最后。【类似于广度优先遍历】

//旅行商问题
public class Main {
    public static void main(String[] args) {
        String city[] = {"A","B","C","D","E","F","G","H","I","J"};
        int x[] = {2066,935,1270,1389,984,2253,949,87,3094,2706};
        int y[] = {2333,1304,200,700,2810,478,3025,2483,1883,3130};
        double distance[][] = Caldistance(x,y);
        solve(distance,city);
    }
    //计算距离
    public static double[][] Caldistance(int x[],int y[]){
        double [][] distance = new double[x.length][x.length];
        for(int i = 0;i<x.length;i++){
            for(int j = 0;j<x.length;j++){
                //欧式距离
                distance[i][j] = Math.sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
            }
        }
        return distance;
    }
    public static void solve(double[][]distance,String []city){
        double sum = 0;
        //记录访问过的城市
        int visited[] = new int[distance.length];
        //记录依次访问的城市,得到最小的距离
        String path[] = new String[distance.length];
        //假设从第一个城市A开始
        path[0] = "A";
        //访问过的点记录为1
        visited[0] = 1;
        //当前正在访问的点
        int k = 0;
        //第一次设下一个城市为Index为1的B城市
        int nextCityIndex = 1;
        //初始化最小的距离
        double mindistance = Double.MAX_VALUE;
        //记录以及访问过多少个城市了
        int count = 1;
        while(count<city.length){
            for(int i = 0;i<city.length;i++){
                //如果该点没被访问
                if(visited[i]==0){
                    //求出 从正在访问的点k到下一个点的最小距离,即为下一个要被访问的城市
                    if(distance[k][i]<mindistance){
                        mindistance  = distance[k][i];
                        //获取下一个要被访问的城市的下标索引
                        nextCityIndex = i;
                    }
                }
            }
            //依次访问的点的总路程
            sum = sum + mindistance;
            //将得到的下一个要访问的城市加入到path中
            path[count++] = city[nextCityIndex];
            //该点被访问,置为1
            visited[nextCityIndex] = 1;
            //将访问的城市起点改成求得的下一个城市的点的坐标索引
            k = nextCityIndex;
            //再次初始化
            mindistance = Double.MAX_VALUE;
        }
        //得出依次访问的结果,输出
        for(int i = 0;i<path.length;i++){
            System.out.print(path[i]+" ");
        }
    }
}

5.哈夫曼构造价值树

哈夫曼见: https://blog.csdn.net/likunkun__/article/details/80258515
相同题型:切金条: https://blog.csdn.net/qq_34115899/article/details/79723970

参考:图解贪婪算法 https://blog.csdn.net/a8082649/article/details/82079779

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值