洛谷题单 算法1-5 贪心

凌乱的yyy / 线段覆盖

题目背景
快 noip 了,yyy 很紧张!

题目描述
现在各大 oj 上有 n 个比赛,每个比赛的开始、结束的时间点是知道的。

yyy 认为,参加越多的比赛,noip 就能考的越好(假的)。

所以,他想知道他最多能参加几个比赛。

由于 yyy 是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加 2 个及以上的比赛。

输入格式
第一行是一个整数 n ,接下来 n行每行是 2 个整数 ai,bi,表示比赛开始、结束的时间。

输出格式
一个整数最多参加的比赛数目。

输入输出样例
输入 #1复制
3
0 2
2 4
1 3
输出 #1复制
2
说明/提示
对于20% 的数据, n≤10。
对于 50%50% 的数据, 10^3 。
对于100% 的数据, 0≤ai<bi ≤10^6 。

思路:贪心,很经典的贪心算法。
可以看做:
在一个数轴上有n条线段,现要选取其中k条线段使得这k条线段两两没有重合部分,问最大的k为多少。
最左边的线段放什么最好?
显然放右端点最靠左的线段最好,从左向右放,右端点越小妨碍越少
其他线段放置按右端点排序,贪心放置线段,即能放就放



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

public class Main {
    static int n;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        Node[] nodes = new Node[n];
        for(int i=0;i<n;i++){
            nodes[i] = new Node(sc.nextInt(),sc.nextInt());
        }
        Arrays.sort(nodes);
        int ans = 0;
        int time = -1;
        for(int i=0;i<n;i++){
            if(nodes[i].begin>=time){
                ans++;
                time=nodes[i].end;
            }
        }
        System.out.print(ans);
    }
}
class Node implements Comparable<Node> {
    int begin,end;

    public Node() {
    }

    public Node(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }

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

删除问题

题目描述
键盘输入一个高精度的正整数N(不超过250位) ,去掉其中任意k个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的N和k,寻找一种方案使得剩下的数字组成的新数最小。

输入格式
n (高精度的正整数)

k(需要删除的数字个数)

输出格式
最后剩下的最小数。

输入输出样例
输入 #1复制
175438
4
输出 #1复制
13
思路:每次比较相邻两位,如果前一位大于后一位就把前一位删掉,每删除一次,便从头开始重新遍历。注意123456这种情况,如果遍历了一边一个数都没删掉,那么就删最后一位。注意在输出的时候,前导0不必输出。



import java.util.*;

public class Main {
    static char[] n ;
    static int k,count;
    static ArrayList<Integer> arrayList ;
    public static void main(String[] args) {
        arrayList = new ArrayList<>();
        Scanner sc = new Scanner(System.in);
        n = sc.next().toCharArray();
        k = sc.nextInt();
        for(char ch: n){
            int temp = ch-'0';
            arrayList.add(temp);
        }
        while(true){
            boolean flag = false;
            for(int i=0;i<arrayList.size()-1;i++){
                if(arrayList.get(i)>arrayList.get(i+1)&&arrayList.get(i)!=0){
                    Integer temp = arrayList.get(i);
                    arrayList.remove(temp);
                    count++;
                    flag = true;
                    break;
                }
            }
            if(count==k){
                break;
            }
            if(!flag){
                Integer temp = arrayList.get(arrayList.size()-1);
                arrayList.remove(temp);
                count++;
            }
        }
        boolean flag = false;
        for(int i=0;i<arrayList.size();i++){
            if(!flag&&arrayList.get(i)==0){
                continue;
            }else{
                flag = true;
                System.out.print(arrayList.get(i));
            }
        }
        if(!flag){
            System.out.print("0");
        }
    }
}

铺设道路

题目描述
春春是一名道路工程师,负责铺设一条长度为 n 的道路。

铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 n 块首尾相连的区域,一开始,第 i 块区域下陷的深度为 di。
春春每天可以选择一段连续区间[L,R] ,填充这段区间中的每块区域,让其下陷深度减少 1。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 0 。

春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 0 。

输入格式
输入文件包含两行,第一行包含一个整数 n,表示道路的长度。 第二行包含 n 个整数,相邻两数间用一个空格隔开,第i 个整数为 di 。

输出格式
输出文件仅包含一个整数,即最少需要多少天才能完成任务。

输入输出样例
输入 #1复制
6
4 3 2 5 3 5
输出 #1复制
9
说明/提示
【样例解释】

一种可行的最佳方案是,依次选择: [1,6]、[1,6]、[1,2]、[1,1]、[4,6]、[4,4]、[4,4]、[6,6]、[6,6]。

【数据规模与约定】

对于 30% 的数据,1≤n≤10 ;
对于 70% 的数据,1≤n≤1000 ;
对于 100% 的数据,1≤n≤100000,0≤di ≤10000 。

思路:递推。。。
用f[i]表示前i个坑所铺设的最少天数

那么要做的只需比较一下当前的a[i](就是坑的深度)和a[i-1],分两种情况:

如果a[i]<=a[i−1],那么在填a[i−1]时就可以顺便把a[i]填上,这样显然更优,所以f[i]=f[i-1];

否则的话,那么在填a[i−1]时肯定要尽量把a[i]一块填上,a[i]剩余的就单独填。。

所以,f[i]=f[i−1]+(a[i]−a[i−1])。

初始化f[1]=a[1],向后推就行了。



import java.util.*;

public class Main {
    static int n;
    static int[] d,ans;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        d = new int[n+1];
        ans = new int[n+1];
        for(int i=1;i<=n;i++){
            d[i] = sc.nextInt();
        }
        ans[1] = d[1];
        for(int i=2;i<=n;i++){
            if(d[i]>d[i-1]){
                ans[i] = ans[i-1] + d[i]-d[i-1];
            }else {
                ans[i] = ans[i-1];
            }
        }
        System.out.print(ans[n]);
    }
}

[AHOI2018初中组]分组

题目描述
小可可的学校信息组总共有n 个队员,每个人都有一个实力值a[i]。现在,一年一度的编程大赛就要到了,小可可的学校获得了若干个参赛名额,教练决定把学校信息组的n 个队员分成若干个小组去参加这场比赛。
但是每个队员都不会愿意与实力跟自己过于悬殊的队员组队,于是要求分成的每个小组的队员实力值连续,同时,一个队不需要两个实力相同的选手。举个例子:[1, 2, 3, 4, 5]是合法的分组方案,因为实力值连续;[1, 2, 3, 5]不是合法的分组方案,因为实力值不连续;[0, 1, 1, 2]同样不是合法的分组方案,因为出现了两个实力值为1 的选手。
如果有小组内人数太少,就会因为时间不够而无法获得高分,于是小可可想让你给出一个合法的分组方案,满足所有人都恰好分到一个小组,使得人数最少的组人数最多,输出人数最少的组人数的最大值。
注意:实力值可能是负数,分组的数量没有限制。
输入格式
输入有两行:
第一行一个正整数n,表示队员数量。
第二行有n 个整数,第i 个整数a[i]表示第i 个队员的实力。
输出格式
输出一行,包括一个正整数,表示人数最少的组的人数最大值。

输入输出样例
输入 #1复制
7
4 5 2 3 -4 -3 -5
输出 #1复制
3
说明/提示
【样例解释】 分为2 组,一组的队员实力值是4,5,2,3,一组是−4,−3,−5,其中最小的组人数为3,可以发现没有比3 更优的分法了。

【数据范围】

对于100%的数据满足:1≤n≤100000,a[i]≤10^9。
思路:栈+贪心
先将数据进行排序,从头到尾依次遍历数组元素。如果发现已经开辟的栈中最后一个元素符合stacks[j].peek== a[i]-1 那么便将a[i]入栈,如果不存在这样的栈,那么便再开辟一个新的栈,并将a[i]入栈。
如果有多个栈符合stacks[j].peek==a[i]-1怎么办呢?我们需要将a[i]加入到最后一个符合条件的栈中。因为他开辟的晚,元素少,为他添加元素,可以提高最少人数组的平均值。



import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;
import java.util.Stack;

public class Main {
    static int[] a;
    static Stack[] stacks = new Stack[100005];
    static int n,min=99999999,count;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        a = new int[n];
        for(int i=0;i<n;i++){
            a[i] = sc.nextInt();
        }
        Arrays.sort(a);
        for(int i=0;i<n;i++){
            int temp = -1;
            for(int j=0;j<count;j++){
                if((a[i]-1)==(int)stacks[j].peek()){
                    temp = j;
                }
            }
            if(temp!=-1){
                stacks[temp].push(a[i]);
            }else {
                stacks[count]=new Stack();
                stacks[count].push(a[i]);
                count++;
            }
        }
        for(int i=0;i<count;i++){
            min = Math.min(min,stacks[i].size());
        }
        System.out.println(min);
    }
}

跳跳!

题目描述
你是一只小跳蛙,你特别擅长在各种地方跳来跳去。
这一天,你和朋友小 F 一起出去玩耍的时候,遇到了一堆高矮不同的石头,其中第 ii 块的石头高度为hi,地面的高度是h0=0。你估计着,从第 i块石头跳到第 j 块石头上耗费的体力值为 (hi - hj) ^ 2,从地面跳到第 i 块石头耗费的体力值是 (hi) ^ 2 。
为了给小 F 展现你超级跳的本领,你决定跳到每个石头上各一次,并最终停在任意一块石头上,并且小跳蛙想耗费尽可能多的体力值。
当然,你只是一只小跳蛙,你只会跳,不知道怎么跳才能让本领更充分地展现。
不过你有救啦!小 F 给你递来了一个写着 AK 的电脑,你可以使用计算机程序帮你解决这个问题,万能的计算机会告诉你怎么跳。
那就请你——会写代码的小跳蛙——写下这个程序,为你 NOIp AK 踏出坚实的一步吧!

输入格式
输入一行一个正整数 n,表示石头个数。

输入第二行 n 个正整数,表示第i块石头的高度 hi 。

输出格式
输出一行一个正整数,表示你可以耗费的体力值的最大值。

输入输出样例
输入 #1复制
2
2 1
输出 #1复制
5
输入 #2复制
3
6 3 5
输出 #2复制
49

思路:将数据进行排序,每次在没有跳过的最大最小值之间跳,此时差的绝对值最大,结果最大。注意要用long类型储存结果。。。



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

public class Main {
    static int n;
    static long ans;
    static int[] h;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        h = new int[n];
        for(int i=0;i<n;i++){
            h[i] = sc.nextInt();
        }
        Arrays.sort(h);
        int index1 = n-1;
        int index2 = 0;
        ans += Math.pow(h[n-1],2);
        while(index1>index2){
            ans+=Math.pow((h[index1]-h[index2]),2);
            index1--;
            if(index1==index2){
                break;
            }
            ans+=Math.pow((h[index1]-h[index2]),2);
            index2++;
        }
        System.out.println(ans);
    }
}

合并果子

题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 3 种果子,数目依次为 1 , 2 , 9 。可以先将 1 、 2 堆合并,新堆数目为3 ,耗费体力为 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 ,耗费体力为 12 。所以多多总共耗费体力 =3+12=15 。可以证明 15 为最小的体力耗费值。

输入格式
共两行。
第一行是一个整数 n(1≤n≤10000) ,表示果子的种类数。

第二行包含 n 个整数,用空格分隔,第 i 个整数 )ai (1≤ai≤20000) 是第 i 种果子的数目。

输出格式
一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2^31
输入输出样例
输入 #1复制
3
1 2 9
输出 #1复制
15
说明/提示
对于30%的数据,保证有n≤1000:

对于50%的数据,保证有n≤5000;

对于全部的数据,保证有n≤10000。

思路:贪心。将数据储存在集合中,每次按照升序进行排序,弹出前两个元素进行合并,记录消耗的体力,将合并后的数据储存进集合,并将弹出的两个元素删掉,循环n-1次即可。



import java.util.*;

public class Main {
    static int n,sum;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        ArrayList<Integer> arrayList = new ArrayList<>();
        for(int i=0;i<n;i++){
            arrayList.add(sc.nextInt());
        }
        for(int i=0;i<n-1;i++){
            Collections.sort(arrayList);
            int t1 = arrayList.get(0);
            int t2 = arrayList.get(1);
            sum+=t1+t2;
            arrayList.add(t1+t2);
            arrayList.remove(new Integer(t1));
            arrayList.remove(new Integer(t2));
        }
        System.out.print(sum);
    }
}

小A的糖果

题目描述
小 A 有 n个糖果盒,第 i 个盒中有 ai颗糖果。
小 A 每次可以从其中一盒糖果中吃掉一颗,他想知道,要让任意两个相邻的盒子中糖的个数之和都不大于 x,至少得吃掉几颗糖。

输入格式
输入的第一行是两个用空格隔开的整数,代表糖果盒的个数 n 和给定的参数 x。

第二行有 n 个用空格隔开的整数,第 i 个整数代表第 i 盒糖的糖果个数 ai 。

输出格式
输出一行一个整数,代表最少要吃掉的糖果的数量。

输入输出样例
输入 #1复制
3 3
2 2 2
输出 #1复制
1

输入 #2复制
6 1
1 6 1 2 0 4
输出 #2复制
11
输入 #3复制
5 9
3 1 4 1 5
输出 #3复制
0

思路:每次判断两个数据的和是否大于了x,如果大于了x,则需要吃掉糖果,此时要先在右边这组数据上减去。



import java.util.*;

public class Main {
    static long n,x,sum;
    static int[] arr;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextLong();
        x = sc.nextLong();
        arr = new int[(int)n];
        for(int i=0;i<n;i++){
            arr[i] = sc.nextInt();
        }
        for(int i=1;i<n;i++){
            if(arr[i]+arr[i-1]>x){
                long temp =arr[i]+arr[i-1]-x;
                sum += temp;
                if(arr[i]>=temp){
                    arr[i] -= temp;
                }else{
                    arr[i] = 0;
                }
            }
        }
        System.out.println(sum);
    }
}

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

题目描述
阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 N(N≤100) 堆金币,第 i 堆金币的总重量和总价值分别是)mi ,vi (1≤mi ,vi≤100)。阿里巴巴有一个承重量T(T≤1000) 的背包,但并没办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?

输入格式
第一行两个整数 N、T
接下来 N 行,每行两个整数 mi ,vi

输出格式
一个整数表示答案,输出两位小数

输入输出样例
输入 #1复制
4 50
10 60
20 100
30 120
15 45
输出 #1复制
240.00

思路:题目叫背包问题,其实是一道贪心题。每次取性价比最高的就好了。


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

public class Main {
    static int n,t,index;
    static double ans;
    static double v,m,d;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        t = sc.nextInt();
        Node[] node = new Node[n];
        for(int i=0;i<n;i++){
            m = sc.nextDouble();
            v = sc.nextDouble();
            d = v/m;
            node[i] = new Node(m,v,d);
        }
        Arrays.sort(node);
        while(t>0&&index<n){
            if(node[index].m<=t){
                ans += node[index].v;
                t -=node[index].m;
                index++;
            }else{
                ans+=(node[index].d*t);
                t = 0;
                node[index].m-=t;
            }
        }
        System.out.printf("%.2f",ans);
    }
}
class Node implements Comparable<Node>{
    double m,v,d;

    public Node() {
    }

    public Node(double m, double v, double d) {
        this.m = m;
        this.v = v;
        this.d = d;
    }

    @Override
    public int compareTo(Node o) {
        return o.d>this.d?1:-1;
    }
}

陶陶摘苹果(升级版)

题目描述
又是一年秋季时,陶陶家的苹果树结了 n 个果子。陶陶又跑去摘苹果,这次他有一个 a 公分的椅子。当他手够不着时,他会站到椅子上再试试。

这次与 NOIp2005 普及组第一题不同的是:陶陶之前搬凳子,力气只剩下 s 了。当然,每次摘苹果时都要用一定的力气。陶陶想知道在 s<0 之前最多能摘到多少个苹果。

现在已知 n 个苹果到达地上的高度 xi ,椅子的高度 a,陶陶手伸直的最大长度 b,陶陶所剩的力气 s,陶陶摘一个苹果需要的力气yi ,求陶陶最多能摘到多少个苹果。

输入格式
第 1 行:两个数 苹果数 n,力气 s。

第 2 行:两个数 椅子的高度 a,陶陶手伸直的最大长度 b。

第 3 行~第 3+n−1 行:每行两个数 苹果高度xi ,摘这个苹果需要的力气 yi 。

输出格式
只有一个整数,表示陶陶最多能摘到的苹果数。

输入输出样例
输入 #1复制
8 15
20 130
120 3
150 2
110 7
180 1
50 8
200 0
140 3
120 2
输出 #1复制
4
思路:先按照高度排序,把摘不到的筛掉,再按照力气排序,直到s<=0为止。



import java.util.*;

public class Main {
    static int n,s,a,b;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        s = sc.nextInt();
        a = sc.nextInt();
        b = sc.nextInt();
        Node[] node = new Node[n];
        for(int i=0;i<n;i++){
            node[i] = new Node();
            node[i].x = sc.nextInt();
            node[i].y = sc.nextInt();
        }
        Arrays.sort(node,new bx());
        int index = n;
        for(int i=0;i<n;i++){
            if(node[i].x>(a+b)){
                index = i;
                break;
            }
        }
        n = index;
        Arrays.sort(node,0,n,new by());
        int ans = 0;
        for(int i=0;i<=n;i++){
            if(node[i].y>s){
                break;
            }else{
                ans++;
                s-=node[i].y;
            }
        }
        System.out.print(ans);
    }
}
class Node{
    int x,y;
}
class bx implements Comparator<Node>{

    @Override
    public int compare(Node o1, Node o2) {
        return o1.x-o2.x;
    }
}
class by implements Comparator<Node>{

    @Override
    public int compare(Node o1, Node o2) {
        return o1.y-o2.y;
    }
}

纪念品分组

题目描述
元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得 的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品, 并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。

你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。

输入格式
共 n+2 行:
第一行包括一个整数 w,为每组纪念品价格之和的上上限。

第二行为一个整数 n,表示购来的纪念品的总件数 G。

第 3∼n+2 行每行包含一个正整数 Pi表示所对应纪念品的价格。

输出格式
一个整数,即最少的分组数目。

输入输出样例
输入 #1复制
100
9
90
20
20
30
50
60
70
80
90
输出 #1复制
6

思路:将数据按照升序排序,从小到大遍历,对于每个p[i] ,我们从大到小寻找一个p[j],使p[i]+p[j]<=w,则这两组数据为一组。如果找不到j,那么第i组数据单独为一组。



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

public class Main {
    static int w,n,ans;
    static int[] p;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        w = sc.nextInt();
        n = sc.nextInt();
        p = new int[n];
        for(int i=0;i<n;i++){
            p[i] = sc.nextInt();
        }
        Arrays.sort(p);
        for(int i=0;i<n;i++){
           if(p[i]>0){
               boolean flag = false;
               for(int j=n-1;j>i;j--){
                   if(p[j]>0&&(p[j]+p[i])<=w){
                       ans++;
                       p[j]=-1;
                       flag = true;
                       break;
                   }
               }
               if(!flag){
                   p[i]=-1;
                   ans++;
               }
           }
        }
        System.out.print(ans);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值