zoukankan      html  css  js  c++  java
  • 洛谷题单 算法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);
        }
    }
    
    
  • 相关阅读:
    【WebService】IIS下部署WebService出现“请求格式无法识别”
    SqlCacheDependency特性
    Frame框架在Html中的应用详解
    一个不错的css——之三行三列等高格局.

    人才战争的书
    2003年三亚游,诸多恶评再一次浮出水面(同事原创)
    转摘记录农民伯伯2011年看的东西
    Office 2010 中打开多个Excel文件只能在同一窗口中显示的问题
    一个50G大小的outlook2010 PST文件
  • 原文地址:https://www.cnblogs.com/fxzemmm/p/14847931.html
Copyright © 2011-2022 走看看