贪心算法(一)

贪心算法

最佳支付方法

1元、5元、10元、20元、100元、200元的钞票支付X元(1,5,10,20,100,200是成倍数关系)
思路:
成倍数关系意味着,大额一定可以用小额表示,所以尽量先用大额。

import java.util.Scanner;

/**
 * @Author: TJW
 * @Description:
 * @Date: 2018/12/9
 */
public class Greedy_coins {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int total = scanner.nextInt();
        int coins[] = new int[]{1,5,10,20,100,200};
        int numOfCoins[] = new int[]{9,8,7,3,1,3};//每一枚硬币最大数目限制
        int sum = 0;
        int left = total;
        for(int i=coins.length-1;i>=0;i--){
            if(left>=0){
                int num = Math.min(numOfCoins[i],left/coins[i]);
                left-=num*coins[i];
                sum+=num;
            }
        }
        System.out.println(sum);
    }
}

如果零钱不是倍数关系,使用动态规划求解,可以看
https://blog.csdn.net/xiaowei_innocence/article/details/84893683

摇摆序列

给定一个序列,找出最长摇摆序列(相邻的数值差呈正负交替出现)
思路:
找到子序列递增或递减的头尾,忽略中间部分。

package GreedyAlgorithm;

/**
 * @Author: TJW
 * @Description:
 * @Date: 2018/12/10
 */
public class Greedy_swing_array {
    /*
    贪心规律: 当序列有一段连续的递增(或递减)时,为形成摇摆子序列,
    我们只需要保留这段连续的递增(或递减)的首尾元素,
    这样更有可能使得尾部的后一个元素成为摇摆子序列的下一个元素。 
     */
    public static void main(String[] args) {
        int[] array = new int[]{1,17,5,10,13,15,10,5,16,8};
        int start = 0;
        int up = 1;
        int down = 2;
        int state = start;
        int length = 1;
        for(int i=1;i<array.length;i++){
            switch (state){
                case 0:
                    if(array[i]>array[i-1]){
                        state = up;
                        length++;
                    }
                    if(array[i]<array[i-1]){
                        state = down;
                        length++;
                    }
                    break;
                case 1:
                    if(array[i]<array[i-1]){
                        state = down;
                        length++;
                    }
                    break;
                case 2:
                    if(array[i]>array[i-1]){
                        state = up;
                        length++;
                    }
                    break;
            }
        }
        System.out.println(length);
    }
}

移除K个数字

已知一个使用字符串表示非负整数num,将num中的k个数字移除,求移除k个数字后,可以获得的最小的可能的新数字
思路:
遍历的数字大于栈顶元素,则将该数字push入栈,如果小于栈顶元素则进行pop弹栈,直到栈为空,或者不能再删除数字(k==0)或栈顶小于当前元素为止。
特殊情况:
第一种特殊情况:一种特殊情况是在栈为空的时候,进去的为0,例如数字是num=100200,此时先取出1,1进入栈,但是第二个0的时候,此时1出栈,但0不能进栈,因为此时栈内为空。
第二种特殊情况:数字是单调递增的时候,例如12345678,此时在入栈完成后,检查栈的大小,如果栈的size()大于原先的字符串中的数字的长度减去2,此时要做处理,直接删除最后的两个数字。

package GreedyAlgorithm;

import org.omg.Messaging.SYNC_WITH_TRANSPORT;

import java.util.Stack;

/**
 * @Author: TJW
 * @Description:
 * @Date: 2018/12/10
 */
public class Greedy_remove_k {
    public static void main(String[] args) {
        String num = "100200";
        int k = 3;
        Stack<Integer> stack = new Stack<>();
        for(int i=0;i<num.length();i++){
            Integer temp = Integer.parseInt(num.substring(i,i+1));
            if(!temp.equals(0)){
                stack.push(temp);
                break;
            }
        }
        /*
        如果遍历的数字大于栈顶元素,则将该数字push入栈,
        如果小于栈顶元素则进行pop弹栈,
        直到栈为空,或者不能再删除数字(k==0)或栈顶小于当前元素为止
         */
        for(int i=1;i<num.length();i++){
            Integer before = stack.pop();
            stack.push(before);
            Integer current = Integer.parseInt(num.substring(i,i+1));
            if(current>=before){
                stack.push(current);
            }else{
                if(current.equals(0)){
                    int temp = stack.pop();
                    k--;
                    while (temp!=0&&!stack.empty()&&k>=0){
                        temp = stack.pop();
                        k--;
                    }
                    stack.push(temp);
                    k++;
                    stack.push(current);

                }else{
                    while(before>current&&k>=0){
                        before = stack.pop();
                        k--;
                    }
                    stack.push(before);
                    k++;
                    stack.push(current);

                }

            }
            /*System.out.println(i+":"+stack);
            System.out.println("k:"+k);*/
        }
        for(int i=0;i<stack.size()&&i<num.length()-k;i++){
            System.out.print(stack.get(i));
        }

    }
}
跳跃游戏

一个数组存储了非负整型数据,数组中第i个元素nums[i],代表了可以从数组第i个位置最多向前跳跃nums[i]步,已知数组各元素的情况下,求是否可以从数组第0个位置跳跃到数组的最后一个元素的位置。
思路:
贪心算法就是在相同选择数目里要走更多的路,故如果选两个num[i1],num[i1+x],0<=x<=num[i],应该要最远路径。如果不可到达,则是要停滞在某个位置。

package GreedyAlgorithm;

/**
 * @Author:  TJW
 * @Description:
 * @Date: 2018/12/10
 */
public class Greedy_min_steps {
    public static void main(String[] args) {
        //int num[] = new int[]{3,2,1,0,4};
        int num[]={2,3,1,1,1,4};
        int count = 0;
        int loc = 0;
        while(loc!=num.length-1){
            int max_step = 0;
            int j = -1;//记录步数
            for(int i=1;i<=num[loc];i++){
                if(max_step<=(num[i]+i)){
                    max_step = num[i]+i;
                    j=i;
                }
            }
            if(j!=-1){
                if(num[j]+j==j&&j!=num.length-1){
                    count=-1;
                    break;
                }
            }
            loc+=j;
            count++;

        }
        if(count==-1){
            System.out.println("cannot access");
        }else{
            System.out.println("steps:"+count);
        }
    }
}

射气球问题

已知在一个平面上有一定数量的气球,平面可以看作是一个坐标系,在平面的x轴的不同位置安排弓箭手向y轴方向射箭,弓箭可以向y轴走无穷远,给定气球的宽度xstart<=x<=xend,问至少需要多少弓箭手,将全部气球打爆?
思路:
对于某个气球,至少需要使用1只弓箭将它击穿,在这只气球将其击穿的同时,尽可能击穿其他更多的气球。(这里体现了贪心的思想)
先按左坐标排序,然后选择看右坐标判断是否重叠,虽然看了几个博客都说要维护一个射手区间,但是射手区间应该只用到了右坐标,和左坐标无关。

package GreedyAlgorithm;

import java.util.ArrayList;
import java.util.Collections;

/**
 * @Author:  TJW
 * @Description:
 * @Date: 2018/12/10
 */
class Node implements Comparable<Node>{

    int left;
    int right;

    public Node(int left, int right){
        this.left = left;
        this.right = right;
    }

    @Override
    public int compareTo(Node o) {
        return left-o.left;
    }
}


public class Greedy_shot {
    public static void main(String[] args) {
        ArrayList<Node> list = new ArrayList<>();
        Node n1 = new Node(10,16);
        Node n2 = new Node(2,8);
        Node n3 = new Node(1,6);
        Node n4 = new Node(7,12);
        list.add(n1);
        list.add(n2);
        list.add(n3);
        list.add(n4);
        Collections.sort(list);
        //int start = n1.left;
        int end = n1.right;
        int count = 1;
        for(int i=1;i<list.size();i++){
            if(list.get(i).left<=end){
                //start = list.get(i).left;
                if(list.get(i).right<end){
                    end = list.get(i).right;
                }
            }else{
                //start = list.get(i).left;
                end = list.get(i).right;
                count++;
            }
        }
        System.out.println("count:"+count);
    }
}

参考:
https://blog.csdn.net/chenkaibsw/article/details/80214911
https://blog.csdn.net/bajin7353/article/details/80703727

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值