2-20算法习题总结

二分查找问题

[COCI 2011/2012 #5] EKO / 砍树

题目描述

伐木工人 Mirko 需要砍 M M M 米长的木材。对 Mirko 来说这是很简单的工作,因为他有一个漂亮的新伐木机,可以如野火一般砍伐森林。不过,Mirko 只被允许砍伐一排树。

Mirko 的伐木机工作流程如下:Mirko 设置一个高度参数 H H H(米),伐木机升起一个巨大的锯片到高度 H H H,并锯掉所有树比 H H H 高的部分(当然,树木不高于 H H H 米的部分保持不变)。Mirko 就得到树木被锯下的部分。例如,如果一排树的高度分别为 20 , 15 , 10 20,15,10 20,15,10 17 17 17,Mirko 把锯片升到 15 15 15 米的高度,切割后树木剩下的高度将是 15 , 15 , 10 15,15,10 15,15,10 15 15 15,而 Mirko 将从第 1 1 1 棵树得到 5 5 5 米,从第 4 4 4 棵树得到 2 2 2 米,共得到 7 7 7 米木材。

Mirko 非常关注生态保护,所以他不会砍掉过多的木材。这也是他尽可能高地设定伐木机锯片的原因。请帮助 Mirko 找到伐木机锯片的最大的整数高度 H H H,使得他能得到的木材至少为 M M M 米。换句话说,如果再升高 1 1 1 米,他将得不到 M M M 米木材。

输入格式

1 1 1 2 2 2 个整数 N N N M M M N N N 表示树木的数量, M M M 表示需要的木材总长度。

2 2 2 N N N 个整数表示每棵树的高度。

输出格式

1 1 1 个整数,表示锯片的最高高度。

样例 #1

样例输入 #1

4 7
20 15 10 17

样例输出 #1

15

样例 #2

样例输入 #2

5 20
4 42 40 26 46

样例输出 #2

36

提示

对于 100 % 100\% 100% 的测试数据, 1 ≤ N ≤ 1 0 6 1\le N\le10^6 1N106 1 ≤ M ≤ 2 × 1 0 9 1\le M\le2\times10^9 1M2×109,树的高度 ≤ 4 × 1 0 5 \le 4\times 10^5 4×105,所有树的高度总和 > M >M >M

代码如下:

package exercise.luogu.binary;

import java.util.Scanner;

public class LumberjackMirko {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 输入树的数量和需要的木材总长度
        int N = scanner.nextInt();
        int M = scanner.nextInt();
        scanner.nextLine(); // 消耗换行符

        // 输入每棵树的高度
        int[] treeHeights = new int[N];
        for (int i = 0; i < N; i++) {
            treeHeights[i] = scanner.nextInt();
        }

        // 输出最大高度
        System.out.println(findMaxHeight(treeHeights, M));
        scanner.close();
    }

    private static int findMaxHeight(int[] treeHeights, int M) {
        int left = 0; // 左边界,最小高度
        int right = Integer.MIN_VALUE; // 右边界,最大高度

        // 找到树的最大和最小高度
        for (int height : treeHeights) {
            if (height > right) {
                right = height;
            }
            if (height < left || left == 0) {
                left = height;
            }
        }

        // 如果所有树的高度都小于M,直接返回0
        if (right < M) {
            return 0;
        }

        // 二分搜索
        while (left < right) {
            int mid = left + (right - left + 1) / 2; // 使用向上取整,确保mid是整数
            if (canGetWood(treeHeights, mid) >= M) {
                // 如果在mid高度可以得到M米木材,则尝试更高的高度
                left = mid;
            } else {
                // 如果不能,则降低高度
                right = mid - 1;
            }
        }
        // 检查left高度是否可以得到M米木材
        return canGetWood(treeHeights, left) >= M ? left : left - 1;
    }

    private static int canGetWood(int[] treeHeights, int height) {
        int wood = 0;
        for (int treeHeight : treeHeights) {
            if (treeHeight > height) {
                wood += treeHeight - height;
            }
        }
        return wood;
    }
}

贪心问题

[NOIP2012 提高组] 国王游戏

题目描述

恰逢 H 国国庆,国王邀请 n n n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n n n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

输入格式

第一行包含一个整数 n n n,表示大臣的人数。

第二行包含两个整数 a a a b b b,之间用一个空格隔开,分别表示国王左手和右手上的整数。

接下来 n n n 行,每行包含两个整数 a a a b b b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

输出格式

一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

样例 #1

样例输入 #1

3 
1 1 
2 3 
7 4 
4 6

样例输出 #1

2

提示

【输入输出样例说明】

1 1 1 2 2 2 3 3 3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 2 2

1 1 1 3 3 3 2 2 2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 2 2

2 2 2 1 1 1 3 3 3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 2 2

按$ 2$、 3 3 3、$1 $这样排列队伍,获得奖赏最多的大臣所获得金币数为 9 9 9

3 3 3 1 1 1、$2 $这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 2 2

按$ 3$、 2 2 2 1 1 1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9 9 9

因此,奖赏最多的大臣最少获得 2 2 2 个金币,答案输出 2 2 2

【数据范围】

对于 20 % 20\% 20% 的数据,有 1 ≤ n ≤ 10 , 0 < a , b < 8 1≤ n≤ 10,0 < a,b < 8 1n10,0<a,b<8

对于 40 % 40\% 40% 的数据,有$ 1≤ n≤20,0 < a,b < 8$;

对于 60 % 60\% 60% 的数据,有 1 ≤ n ≤ 100 1≤ n≤100 1n100

对于 60 % 60\% 60% 的数据,保证答案不超过 1 0 9 10^9 109

对于 100 % 100\% 100% 的数据,有 1 ≤ n ≤ 1 , 000 , 0 < a , b < 10000 1 ≤ n ≤1,000,0 < a,b < 10000 1n1,000,0<a,b<10000

NOIP 2012 提高组 第一天 第二题

分治问题:

[USACO07JAN] Balanced Lineup G

题目描述

For the daily milking, Farmer John’s N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.

Farmer John has made a list of Q (1 ≤ Q ≤ 180,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.

每天,农夫 John 的 n ( 1 ≤ n ≤ 5 × 1 0 4 ) n(1\le n\le 5\times 10^4) n(1n5×104) 头牛总是按同一序列排队。

有一天, John 决定让一些牛们玩一场飞盘比赛。他准备找一群在队列中位置连续的牛来进行比赛。但是为了避免水平悬殊,牛的身高不应该相差太大。John 准备了 q ( 1 ≤ q ≤ 1.8 × 1 0 5 ) q(1\le q\le 1.8\times10^5) q(1q1.8×105) 个可能的牛的选择和所有牛的身高 h i ( 1 ≤ h i ≤ 1 0 6 , 1 ≤ i ≤ n ) h_i(1\le h_i\le 10^6,1\le i\le n) hi(1hi106,1in)。他想知道每一组里面最高和最低的牛的身高差。

输入格式

Line 1: Two space-separated integers, N and Q.

Lines 2…N+1: Line i+1 contains a single integer that is the height of cow i

Lines N+2…N+Q+1: Two integers A and B (1 ≤ A ≤ B ≤ N), representing the range of cows from A to B inclusive.

第一行两个数 n , q n,q n,q

接下来 n n n 行,每行一个数 h i h_i hi

再接下来 q q q 行,每行两个整数 a a a b b b,表示询问第 a a a 头牛到第 b b b 头牛里的最高和最低的牛的身高差。

输出格式

Lines 1…Q: Each line contains a single integer that is a response to a reply and indicates the difference in height between the tallest and shortest cow in the range.

输出共 q q q 行,对于每一组询问,输出每一组中最高和最低的牛的身高差。

样例 #1

样例输入 #1

6 3
1
7
3
4
2
5
1 5
4 6
2 2

样例输出 #1

6
3
0

代码如下:

自己写的只过三个点-30分

package exercise.luogu.fenzhi;

import java.util.*;

public class P2880 {
    public static int[] nius;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int q = sc.nextInt();
        nius = new int[n];
        for (int i = 0; i < nius.length; i++) {
            nius[i] = sc.nextInt();
        }
        for (int i = 0; i < q; i++) {
            int m1 = sc.nextInt();
            int m2 = sc.nextInt();
            int result = sort(m1, m2);
            System.out.println(result);

        }

    }

    public static int sort(int left, int right) {
        Set<Integer> set = new TreeSet<>();

        if (left == right) {
            return 0;
        }
        for (int i = left - 1; i <= right - 1; i++) {
            set.add(nius[i]);
        }
        List<Integer> list = new ArrayList<>(set);
        return list.get(list.size() - 1) - list.get(0);
    }
}

搜的也只有60分,有三个点不过

import java.util.Scanner;

public class P2880 {
    private static int[][] maxST; // ST表,存储最大值  
    private static int[][] minST; // ST表,存储最小值  
    private static int[] nums; // 原始数组  

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // 数组大小  
        int q = sc.nextInt(); // 查询次数  
        nums = new int[n];

        // 读取数组元素  
        for (int i = 0; i < n; i++) {
            nums[i] = sc.nextInt();
        }

        // 初始化ST表  
        maxST = new int[n][(int) (Math.log(n) / Math.log(2)) + 1];
        minST = new int[n][(int) (Math.log(n) / Math.log(2)) + 1];
        preprocessST(n);

        // 执行查询  
        for (int i = 0; i < q; i++) {
            int left = sc.nextInt() - 1; // 查询左端点(从0开始计数)  
            int right = sc.nextInt() - 1; // 查询右端点(从0开始计数)  
            int maxVal = queryMax(left, right);
            int minVal = queryMin(left, right);
            System.out.println(maxVal - minVal); // 输出区间最大值与最小值之差  
        }

        sc.close();
    }

    // 预处理ST表,计算每个区间的最大值和最小值  
    private static void preprocessST(int n) {
        for (int i = 0; i < n; i++) {
            maxST[i][0] = nums[i];
            minST[i][0] = nums[i];
        }

        for (int j = 1; (1 << j) <= n; j++) {
            for (int i = 0; i + (1 << j) <= n; i++) {
                maxST[i][j] = Math.max(maxST[i][j - 1], maxST[i + (1 << (j - 1))][j - 1]);
                minST[i][j] = Math.min(minST[i][j - 1], minST[i + (1 << (j - 1))][j - 1]);
            }
        }
    }

    // 查询区间[left, right]的最大值  
    private static int queryMax(int left, int right) {
        int k = (int) (Math.log(right - left + 1) / Math.log(2));
        return Math.max(maxST[left][k], maxST[right - (1 << k) + 1][k]);
    }

    // 查询区间[left, right]的最小值  
    private static int queryMin(int left, int right) {
        int k = (int) (Math.log(right - left + 1) / Math.log(2));
        return Math.min(minST[left][k], minST[right - (1 << k) + 1][k]);
    }
}

对于动态规划,我都想放弃了,针对好难啊!!!

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知意..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值