洛谷刷题集合 | 贪心 |


此题目题单出自洛谷题单贪心算法:
谨以此题解博客记录当时刷题时遇到的种种困难以及心得!!


P2240 【深基12.例1】部分背包问题

先对每一堆金币以单位价值排序,然后按顺序拿,实现局部最优解,最后如果剩余的重量不足一堆金币的重量,就选取部分进行切割。
这也是这道题和其他背包问题的不同之处!!
import java.util.Scanner;
public class P2240 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int N=sc.nextInt(),T=sc.nextInt(),sum_m=0;
        double sum_v=0;
        Node[] node=new Node[N];
        for (int i = 0; i < N; i++) {
            node[i]=new Node(sc.nextDouble(),sc.nextDouble());
        }
        arrSort(node);
        for (int i = 0; i <N; i++) {
            if(sum_m<T)//如果重量不够 就继续选
            {
                if(T-sum_m>=node[i].m)
                {
                    sum_m+=node[i].m;
                    sum_v+=node[i].v;
                }else{
                    sum_v+=node[i].sort()*(T-sum_m);
                    sum_m+=node[i].m;
                }
            }else {
                System.out.format("%.2f",sum_v);
                return;
            }
        }
        System.out.format("%.2f",sum_v);

        }
    //给对象排序
    static void arrSort(Node[] node){
        for (int i = 0; i < node.length; i++) {
            for (int j = 0; j < node.length; j++) {
                if(node[i].sort()>node[j].sort())
                {
                    Node temp=node[i];
                    node[i]=node[j];
                    node[j]=temp;
                }
            }
        }
    }
}
class Node{
    double m;
    double v;

    public Node(double m,double v){
        this.m=m;
        this.v=v;
    }
    public double sort(){
        return v/m;
    }
}

P1223 排队接水

这个题主要是得出 如何接水能使平均等待时间最小,推算发现,接水时间少的人先接水会使下一个人的等待接水的时间更少。
因此 直接排序,然后从大到小进行接水就好了。
import java.util.Scanner;

//言简意赅  用个对象 十分简单  OHHHHHHH 这个题 算平均时间
//这个等待时间
public class P1223 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        double sum=0;
        Wait[] waits=new Wait[n];
        for (int i = 0; i < n; i++) {
            waits[i]=new Wait(sc.nextInt(),i+1);
        }
        arrSort(waits);
        //按顺序打印
        for (int i = 0; i < n; i++) {
            System.out.print(waits[i].sno+" ");
            if(i!=n-1)
            sum+=waits[i].wait*(n-1-i);
        }
        System.out.println();

        System.out.format("%.2f",sum/n);

    }
    //给对象排序
    static void arrSort(Wait[] waits){
        for (int i = 0; i < waits.length; i++) {
            for (int j = 0; j <waits.length; j++) {
                if(waits[i].wait<waits[j].wait)
                {
                    Wait temp=waits[i];
                    waits[i]=waits[j];
                    waits[j]=temp;
                }
            }
        }
    }
}
class Wait{
    int sno;
    int wait;

    public Wait(int wait,int sno){
        this.wait=wait;
        this.sno=sno;
    }

}

P1803 凌乱的yyy / 线段覆盖

解法:
	观察题目所给的起始时间和结束时间,最终发现,只要在连续的时间段内,yyy的下一场比赛开始时间在这一场比赛的结束时间中就完全OK,
	接下来就是将比赛的时间以结束时间进行排序,然后进行筛选即可!
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        Games[] games=new Games[n];

        for (int i = 0; i < n; i++) {
            int s=sc.nextInt();
            int t=sc.nextInt();
            games[i]=new Games(s,t);
        }
        Arrays.sort(games,new CompareTo());
        int count=0;
        int tt=0;
        for (int i = 0; i < n; i++) {
            if (tt<=games[i].s){//前一个数的尾小于当前的头
                count++;
                tt=games[i].t;//取尾
            }
        }
        System.out.println(count);
    }

}
class Games{
    public int s;//开始时间
    public int t;//结束时间
    public Games(int s, int t) {
        this.s=s;
        this.t=t;
    }
}
class CompareTo implements Comparator<Games>{
    public int compare(Games t1,Games t2){
        return t1.t-t2.t;
    }
}

P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G

构成贪心: 每次移动小的果子堆所需力气最小,因此先移动小的果堆,那么得出方案!
 对果堆进行排序 然后从小果堆开始排序  提交 然后发现会超时 然后利用小根堆进行排序

import java.util.PriorityQueue;
import java.util.Scanner;

//小根堆版本
public class P1090_plus {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        PriorityQueue<Integer> arr=new PriorityQueue();

        int left=0,right=0,sum_1=0,sum_2=0;
        for (int i = 0; i < n; i++) {
            arr.add(sc.nextInt());
        }
        for (int i = 1; i < n; i++) {
            left=arr.poll();
            right=arr.poll();

            sum_1=left+right;
            arr.add(sum_1);
            sum_2+=sum_1;

        }
        System.out.println(sum_2);

    }
}

P3817 小A的糖果

如题意:局部贪心思维,先判断 第i个糖果是否大于x(给定的参数)  如果大于x就先用第i个糖果的数目减去x,如果小于,那么就判断第i项和第i+1项的和是否大于x,如果大于,那么就用他们的和减去x得出来的结果就是第i+1项糖果减少的数目,以此类推,累计全局。
import java.math.BigInteger;
import java.util.Scanner;

//根据题目来写就完了
public class P3817 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),x=sc.nextInt();
        int[] arr=new int[n];
        long sum=0;
        for (int i = 0; i < arr.length; i++) {
            arr[i]=sc.nextInt();
        }
        for (int i = 0; i < arr.length-1; i++) {
            if(arr[i]+arr[i+1]>x){
                  if(arr[i]>=x){

                      sum+=arr[i+1]+(arr[i]-x);
                      arr[i+1]=0;
                  }else{
                      int min=arr[i+1]+arr[i]-x;
                      sum+=min;
                      arr[i+1]=arr[i+1]-min;
                  }
            }
        }
        System.out.println(sum);
    }
}	

P1106 删数问题

原本以为是道简单的贪心题  没想到..... 自己坑自己好多次

第一种错误的的想法来自于普遍贪心题的影响,以为是一道简单的删除最大值的题。
比如: 175438   4
第一次删除最大的数目    8   第二次是  7  第三次是  5  第四次是  4 
然后求得数 为 13  看似没有什么问题  
去提交 过了三个数据项 
然后开始深思 
发现了	 如果数据项是     50074897  2
使用第一种方法   删除  最大的  那就是     500747  然后并不如 删除 5和 7 得  4897小  
这就有了第二种思想了  

第一种错误代码:

import java.util.Scanner;
public class P1106 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        String str=sc.next();
        int n=sc.nextInt();
        char[] arr= str.toCharArray();
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                if(Integer.parseInt(String.valueOf(arr[i]))<Integer.parseInt(String.valueOf(arr[j]))){
                    char temp=arr[i];
                    arr[i]=arr[j];
                    arr[j]=temp;
                }
            }
        }

        StringBuffer stringBuffer=new StringBuffer();
        for (int i = 0; i < arr.length-n; i++) {
            stringBuffer.append(arr[i]);
        }
        System.out.println(Integer.parseInt(stringBuffer.toString()));
    }
}

第二种想法是,如果删除n个数 ,那我就使用补空的方法,比如  175438   4  删除4个数 那就是要求   数据位数为2  的最小数
进行筛选就好了  然并卵
第三种  正确的做法

第二种错误方法:

import java.util.Scanner;

public class P1106 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        String str=sc.next();
        int k=sc.nextInt();
        //先计算出有多少个数字
        int n=str.length()-k;
        char[] arr=str.toCharArray();
        sort(arr);
        StringBuffer abs=new StringBuffer();
        String a=null;
        //然后对每一位进行筛选 看是否符合
        //这里需要将输入的字符串进行排序
        for (int i = 0; i < k; i++) {
            //把最小的装进去,然后再删除最小的前面的 然后检测剩下的位数  如果恰好是n位  就退出循环
            if(i==0)
            {
                arr=delete(str,arr[0]);
            }else{
                arr=delete(a,arr[0]);
            }
            a=new String(String.valueOf(arr));
            sort(arr);
        }
        System.out.println(Integer.parseInt(abs.toString()));

    }

    static void sort(char[] arr){
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                if(Integer.parseInt(String.valueOf(arr[i]))<Integer.parseInt(String.valueOf(arr[j]))){
                     char temp=arr[i];
                     arr[i]=arr[j];
                     arr[j]=temp;
                }
            }
        }
    }

    static char[] delete(String str,char arr){
        //先得出这个字母在这个字符串中的索引
        int i=str.indexOf(arr);
        System.out.println(i);
        StringBuffer stringBuffer=new StringBuffer(str);
        stringBuffer.delete(0,i);
        String b=new String(stringBuffer.toString());
        return b.toCharArray();
    }
}

发现规律  ,每一个案例, 要被删除的数的后一个数小于它,比如  175438   要被删除的是  7  5 4   8
我们毫无意外的发现了  每一个数都大于他的低位  
8的话  如果我们把8后面的数看为0呢  那他也大于他的高位  
因此有了以下代码,终得AC

第三种正确方法:

import java.util.Scanner;
public class P1106 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str = sc.next();
        int k = sc.nextInt();
        int n=str.length()+1;
        //转换为数组
        char[] arr =new char[n];
        for (int i = 0; i < arr.length; i++) {
            if(i==arr.length-1){
                arr[i]='0';
            }else{
                arr[i]=str.charAt(i);
            }
        }

        // 1 7 5 4 3 8
        for (int i = 0; i < k; i++) {
            for (int j = 0; j < n-1; j++) {
                if(Integer.parseInt(String.valueOf(arr[j]))>Integer.parseInt(String.valueOf(arr[j+1])))
                {
                    for (int l =j; l <n-1; l++) {
                        arr[l]=arr[l+1];
                    }
                    n--;
                    break;
                }
            }
        }

        StringBuffer stringBuffer=new StringBuffer();
        for (int i = 0; i <n-1; i++) {
            stringBuffer.append(arr[i]);
        }

        System.out.println(Integer.parseInt(stringBuffer.toString()));

    }
}

P1478 陶陶摘苹果(升级版)

这道题的想法是:  直接比较判断淘淘不能摘的苹果,然后对剩下的苹果所要使用的力气进行排序,然后遍历就ok。

代码如下:

import java.util.Scanner;
public class P1478 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),s=sc.nextInt(),a=sc.nextInt(),b=sc.nextInt();
        Apple[] apples=new Apple[n];
        int cnt=0;
        for (int i = 0; i <n ; i++) {
            int x=sc.nextInt(),y=sc.nextInt();
                apples[i]=new Apple(x,y);
        }

        sort(apples);
        for (int i = 0; i < n; i++) {
            if(s>=apples[i].y && apples[i].x<=(a+b)){
                s-=apples[i].y;
                cnt++;
            }
        }
        System.out.println(cnt);


    }

    static  void sort(Apple[] apples)
    {
        for (int i = 0; i < apples.length; i++) {
            for (int j = 0; j < apples.length; j++) {
                if(apples[i].y<apples[j].y){
                    Apple apple=apples[i];
                    apples[i]=apples[j];
                    apples[j]=apple;
                }
            }
        }
    }
}

class Apple{
     int x;
     int y;

    public Apple(int x,int y){
        this.x=x;
        this.y=y;
    }

}

P5019 [NOIP2018 提高组] 铺设道路

第一种想法,4 3 2 5 3 5   
最小值归零法  求出连续片段的最小值  然后填坑
2——>2 1 0 3 1 3
1——>1 0 0 3 1 3
1——>0 0 0 3 1 3
1——>0 0 0 2 0 2
1——>0 0 0 1 0 2
1——>0 0 0 0 0 2
1——>0 0 0 0 0 1
1——>0 0 0 0 0 0
暂时没有实现,以后会有优化!wa wa !! 等到以后复习到二叉树的时候 用二叉树来解决!!
第二种思路:

代码如下:

P1208 [USACO1.3]混合牛奶 Mixing Milk

按照单价进行排序,先收纳单价便宜的,然后遍历就ok。
import java.util.Scanner;
/*背包问题*/
//拿呗  先排序 然后拿  用对象来存数据
public class P1208 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),m=sc.nextInt(),sum=0;
        int sum_m=0;
        Milk[] milk=new Milk[m];
        for (int i = 0; i < m; i++) {
            milk[i]=new Milk(sc.nextInt(),sc.nextInt());
        }
        arrSort(milk);
        for (int i = 0; i <m; i++) {
            if(sum_m<=n)//如果重量不够 就继续选
            {
                if(n-sum_m>=milk[i].a)
                {
                    sum_m+=milk[i].a;
                    sum+=milk[i].p*milk[i].a;
                }else{
                    sum+=milk[i].p*(n-sum_m);
                    sum_m+=milk[i].a;
                }
            }else {
                System.out.println(sum);
                return;
            }

        }
        System.out.println(sum);

    }

    //给对象排序
    static void arrSort(Milk[] node){
        for (int i = 0; i < node.length; i++) {
            for (int j = 0; j < node.length; j++) {
                if(node[i].p<node[j].p)
                {
                    Milk temp=node[i];
                    node[i]=node[j];
                    node[j]=temp;
                }
            }
        }
    }

}

class Milk{
    int p;
    int a;

    public Milk(int m,int v){
        this.p=m;
        this.a=v;
    }

    @Override
    public String toString() {
        return "Milk{" +
                "p=" + p +
                ", a=" + a +
                '}';
    }
}

P4995 跳跳!

这道题的思路是:
	从地面跳的话,肯定是石头最高费的力气最大,因此将石头进行排序,开始跳想最高的石头
	然后就从ai 和 an-i个石头来回眺,这样的差值是最大的,所需要的力气也是最大的。
	
arr[1]-arr[0]
3个数
arr[2]-arr[0]  arr[2]-arr[1]
4个数
 arr[3]-arr[0]  arr[2]-arr[0]  arr[2]-arr[1]
5个数
arr[4]-arr[0]  arr[3]-arr[0]  arr[3]-arr[1]  arr[2]-arr[1]
6个数
arr[5]-arr[0] arr[4]-arr[0]  arr[4]-arr[1]  arr[3]-arr[1]  arr[3]-arr[2]
7个数
arr[6]-arr[0]  arr[5]-arr[0]  arr[5]-arr[1]  arr[4]-arr[1]  arr[4]-arr[2]   arr[3]-arr[2]
将他们两两排列:
 arr[6]-arr[0]      arr[0]-arr[5]
 arr[5]-arr[1]      arr[1]-arr[4]
 arr[4]-arr[2]      arr[2]-arr[3]
发现规律:  m初始值为n-1,  n为0
 sum+=(arr[m]-arr[n])*(arr[m]-arr[n]);
 m--;
 sum+=(arr[n]-arr[m])*(arr[n]-arr[m]);
 n++;

代码如下:

import java.util.Arrays;
import java.util.Scanner;

public class P4995 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int[] arr=new int[sc.nextInt()];
        for (int i = 0; i < arr.length; i++) {
            arr[i]=sc.nextInt();
        }
        int m=arr.length-1,n=0,s=0;
        //进行排序
        Arrays.sort(arr);
        long sum=arr[arr.length-1]*arr[arr.length-1];
        if(arr.length-1%2==0)
        {
            for (int i =0; i>(arr.length-1)/2 ; i++) {
                sum+=(arr[m]-arr[n])*(arr[m]-arr[n]);
                m--;
                sum+=(arr[n]-arr[m])*(arr[n]-arr[m]);
                n++;
            }
        }else{
            for (int i =1; i<=((arr.length-1)/2)+1 ; i++) {
                sum+=(arr[m]-arr[n])*(arr[m]-arr[n]);
                m--;
                if(i!=((arr.length-1)/2)+1)
                {
                    sum+=(arr[n]-arr[m])*(arr[n]-arr[m]);
                    n++;
                }
            }
        }
        System.out.println(sum);
    }
}

优化缩短 代码如下:

import java.util.Arrays;
import java.util.Scanner;

public class P4995 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int[] arr=new int[sc.nextInt()];
        for (int i = 0; i < arr.length; i++) 
            arr[i]=sc.nextInt();
        int m=arr.length-1,n=0;
       	Arrays.sort(arr);
        long sum=arr[arr.length-1]*arr[arr.length-1];
        while(m>n){
            sum+=(arr[m]-arr[n])*(arr[m]-arr[n]);
            m--;
            sum+=(arr[n]-arr[m])*(arr[n]-arr[m]);
            n++;
        }
        System.out.println(sum);
    }

}

做这个题的时候呆了一下,用了好几种方法尝试,去发散思维,最后发现还是递推香!!

寻找贪心的推导部分是解贪心最关键的步骤。


总要学会,为什么不学的好一点。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值