能力全面提升综合题单-练习

Part1

语言基础题

P1089 [NOIP 2004 提高组] 津津的储蓄计划

image

import java.util.Scanner;

// P1089 [NOIP 2004 提高组] 津津的储蓄计划
public class P1089 {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int[] budget = new int[12];
        for (int i = 0; i < 12; i++) {
            budget[i] = in.nextInt();
        }
        int money = 0;
        int bank_money = 0;
        for (int i = 0; i < 12; i++) {
            money += 300;
            if (money >= budget[i]) {
                money -= budget[i];
                int save_money = money / 100 * 100;
                money -= save_money;
                bank_money += save_money;
            } else {
                System.out.printf("-%d", i + 1);
                return;
            }
        }
        System.out.println((int) (bank_money * 1.2 + money));
    }
}

image

关于文件读取:
使用 Reader 的子类 BufferedReader (读取大文件)

        int[] budget = new int[12];
        File file = new File("src/input.txt");
        try(BufferedReader br = new BufferedReader(new FileReader(file))) {
        for (int i = 0; i < 12; i++) {
            budget[i]= Integer.parseInt(br.readLine());
        }
    } catch (Exception e) {

    };
        for (int i = 0; i < 12; i++) {
        System.out.println(budget[i]);
    }

P1307 [NOIP 2011 普及组] 数字反转

image

使用 StringBuilderreverse
然后
1.判断正负,负号拿掉
2.反转
3.找到 firstNonZero 第一个非负数字的索引
4.截取,只要后半段

import java.util.Scanner;
import java.util.Stack;

// P1307 [NOIP 2011 普及组] 数字反转
public class P1307 {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String s = in.nextLine();
        StringBuilder sb = new StringBuilder(s);
        boolean negative = sb.charAt(0) == '-';
        if (negative) {
            sb = sb.deleteCharAt(0);
        }
        sb = sb.reverse();

        // 去除前导0
        int firstNonZero = 0; // 第一个非零数字的索引
        while (sb.charAt(firstNonZero) == '0' && firstNonZero < sb.length()) {
            firstNonZero++;
        }
        String out;
        if (negative) {
            out = "-" + sb.substring(firstNonZero);
        } else {
            out = sb.substring(firstNonZero);
        }
        System.out.println(out);
    }
}

数组

P1047 [NOIP 2005 普及组] 校门外的树

image

正常做法(通过70%)(注意最后结果 +1,因为 0 到 l 有 l+1 个树):

public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int l = scanner.nextInt();
        int[] map = new int[l];
        Arrays.fill(map,1); // 1 tree; 0 not tree
        int m = scanner.nextInt();
        for (int i = 0; i < m; i++) {
            int left = scanner.nextInt();
            int right = scanner.nextInt();
            for (int j = left; j <= right; j++) {
                map[j]=0;
            }
        }
        int res = 0;
        for (int i = 0; i < l; i++) {
            res+=map[i];
        }
        System.out.println(res+1);
    }

优化(多个区间操作 -> 差分数组

差分数组前缀和 是一对互逆运算:

  • 前缀和:用于快速 计算 某个范围 [L, R] 内的和
  • 差分数组:用于快速 修改 某个范围 [L, R] 内的值
    image

image
image
image

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    int l = scanner.nextInt();
    int[] map = new int[l+1];
    int[] d = new int[l+2]; // 差分数组 防止 d[right+1] 溢出
    Arrays.fill(map,1); // 1 tree; 0 not tree
    int m = scanner.nextInt();
    for (int i = 0; i < m; i++) {
        int left = scanner.nextInt();
        int right = scanner.nextInt();
        d[left]-=1;
        d[right+1]+=1;
    }
    // 计算前缀和
    map[0] = map[0] + d[0];
    for (int i = 1; i <= l; i++) {
        map[i]=map[i-1]+d[i];
    }
    // 统计
    int res =0;
    for (int i = 0; i <= l; i++) {
        if (map[i]>0)
            res++;
    }
    System.out.println(res);
}

P2141 [NOIP 2014 普及组] 珠心算测验

image

注意读题,是 有多少个数 在前

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    HashSet<Integer> hashSet = new HashSet<>();
    int[] nums = new int[n];
    for (int i = 0; i < n; i++)
        nums[i] = scanner.nextInt();
    for (int i = 0; i < n - 1; i++) {
        for (int j = i + 1; j < n; j++) {
            hashSet.add(nums[i] + nums[j]);
        }
    }
    int res = 0;
    for (int i = 0; i < n; i++) {
        if (hashSet.contains(nums[i])) res++;
    }
    System.out.println(res);
}

字符串

P1055 [NOIP 2008 普及组] ISBN 号码

image

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    String string = scanner.nextLine(); // 0-670-82162-0
    String[] strings = string.split("-"); // 0 670 82162 0
    String concat = strings[0].concat(strings[1]).concat(strings[2]); // 0670821620
    int sum = 0;
    for (int i = 0; i < 9; i++) {
        Integer num = concat.charAt(i) - '0'; // 字符数字转为数字
        sum += num * (i + 1);
    }
    sum = sum % 11;
    // 校验位 需要判断X
    int check = Objects.equals(strings[3], "X") ? 10 : Integer.parseInt(strings[3]);
    if (sum == check) {
        System.out.println("Right");
    } else {
        // 拼接
        String newString = null;
        if (sum == 10) {
            newString = strings[0] + "-" + strings[1] + "-" + strings[2] + "-X";
        } else {
            newString = strings[0] + "-" + strings[1] + "-" + strings[2] + "-" + sum;
        }
        System.out.println(newString);
    }
}

基础算法

模拟

P1003 [NOIP 2011 提高组] 铺地毯

image

存放所有的地毯坐标 -> 内存超出

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    HashMap<String, Integer> hashMap = new HashMap<>(); // key坐标 value地毯编号
    for (int i = 0; i < n; i++) {
        int a = scanner.nextInt();
        int b = scanner.nextInt();
        int g = scanner.nextInt();
        int k = scanner.nextInt();
        hashMap = updateMap(hashMap, a, b, a + g - 1, b + k - 1, i + 1);
    }
    int x = scanner.nextInt();
    int y = scanner.nextInt();
    if (hashMap.containsKey(x + "," + y)) {
        System.out.println(hashMap.get(x + "," + y));
    } else {
        System.out.println(-1);
    }
}
private static HashMap<String, Integer> updateMap(HashMap<String, Integer> map, int x, int y, int x2, int y2, int carpet) {
    for (int i = x; i <= x2; i++) {
        for (int j = y; j <= y2; j++) {
            map.put(i + "," + j, carpet);
        }
    }
    return map;
}

不存坐标,而是直接存地毯对象

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    static class Carpet {
        int x, y, width, height, id;

        Carpet(int x, int y, int width, int height, int id) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
            this.id = id;
        }

        boolean covers(int px, int py) {
            return px >= x && px < x + width && py >= y && py < y + height;
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        List<Carpet> carpets = new ArrayList<>();

        for (int i = 0; i < n; i++) {
            int a = scanner.nextInt();
            int b = scanner.nextInt();
            int g = scanner.nextInt();
            int k = scanner.nextInt();
            carpets.add(new Carpet(a, b, g, k, i + 1)); // 存储地毯的范围信息
        }

        int x = scanner.nextInt();
        int y = scanner.nextInt();

        // 从后往前遍历,找到最上面的地毯
        for (int i = carpets.size() - 1; i >= 0; i--) {
            if (carpets.get(i).covers(x, y)) {
                System.out.println(carpets.get(i).id);
                return;
            }
        }

        System.out.println(-1); // 没有地毯覆盖 (x, y)
    }
}

二分答案

二分答案适用:有序 很多可行解中寻求最优解

二分查找的核心思想就是在大量可行解中,通过一个具有单调性(或单调趋势)的判断函数,快速缩小搜索范围,从而找到最优解

例如,在跳石头问题中,我们通过判断函数验证某个候选最小跳跃距离是否可行,再根据判断结果调整搜索区间,最终确定最大的最小跳跃距离。这种方法在很多问题中都适用,只要能证明随着候选值变化,问题的可行性也呈现出单调性趋势,就可以用二分查找来求最优解。

P2678 [NOIP 2015 提高组] 跳石头

image

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

public class P2678_stoneJump {
    // 全局变量:l 表示河流总长度,n 表示中间石头数量,m 表示允许移除的石头数量
    static int l;
    static int n;
    static int m;
    // 数组 map 用于存放所有石头的位置(包括起点和终点),大小足够大
    static int[] map = new int[50001];

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 读入河流总长度、石头数量(不含起点和终点)以及允许移除的石头数
        l = scanner.nextInt();
        n = scanner.nextInt();
        m = scanner.nextInt();
        
        // 读入中间石头的位置,存入 map 数组,索引从 1 开始
        for (int i = 1; i <= n; i++) {
            map[i] = scanner.nextInt();
        }
        
        // 将起点(位置0)加入数组
        map[0] = 0;  
        // 将终点(位置 l)加入数组
        map[n + 1] = l;  
        // 更新石头总数:原来的 n 个中间石头加上起点和终点
        n += 2;
        
        // 如果输入的石头位置没有保证有序,需要对 map 数组进行排序
        // Arrays.sort(map, 0, n);

        // 二分查找的初始左右边界
        int left = 1;      // 最小可能的跳跃距离为 1
        int right = l;     // 最大可能的跳跃距离为河流总长 l
        int ans = -1;      // 用于记录满足条件的最大最小跳跃距离

        // 二分查找过程:当 left <= right 时持续查找
        while (left <= right) {
            // 计算当前候选的最小跳跃距离 mid,采用防止溢出的方法
            int mid = left + (right - left) / 2;
            
            // 调用 judge 函数判断当最小跳跃距离为 mid 时,能否在不超过 m 次移除石头的条件下完成跳跃
            if (judge(mid)) {
                // 如果可行,则记录 mid 作为当前答案,并尝试更大距离
                ans = mid;
                left = mid + 1;
            } else {
                // 如果不可行,则减小候选范围
                right = mid - 1;
            }
        }
        
        // 输出最终得到的最大最小跳跃距离
        System.out.println(ans);
    }

    // judge 函数判断候选最小跳跃距离 x 是否可行
    // 贪心思想:从起点开始,若相邻两个保留石头之间距离小于 x,则移除当前石头
    // 最后统计需要移除的石头数是否不超过允许的 m 个
    private static boolean judge(int x) {
        int count = 0;  // 用于统计移除的石头数
        int now = 0;    // now 表示当前保留石头的下标(初始为起点 0)
        
        // 遍历所有石头,从索引 1 开始判断
        for (int i = 1; i < n; i++) {
            // 如果当前石头与上一个保留石头之间的距离小于候选距离 x,
            // 则认为当前石头无法作为跳跃点,需将其移除
            if (map[i] - map[now] < x) {
                count++;
                // 若移除的石头数超过允许的 m,直接返回 false
                if(count > m) {
                    return false;
                }
            } else {
                // 否则,保留当前石头,更新 now 为当前下标
                now = i;
            }
        }
        // 判断移除的石头数是否在允许范围内
        return count <= m;
    }
}

分治

P1226 【模板】快速幂

image

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    long a = scanner.nextLong();
    long b = scanner.nextLong();
    long p = scanner.nextLong();
    System.out.printf("%s^%s mod %s=%s", a, b, p, power(a, b, p));
    //2^10 mod 9=7
}
private static long power(long a, long b, long p) {
    if (b == 0)
        return 1;
    long temp = power(a, b / 2, p);  // 只计算一次
    if (b % 2 == 0)
        return temp * temp % p;
    else
        return (temp * temp % p) * a % p;  // 注意溢出,分步取余
}

P3612 [USACO17JAN] Secret Cow Code S

image

找递推公式,二分(分治)

static long slen;
public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    String s = scanner.next();
    long n = scanner.nextLong();
    slen = s.length();
    long len = s.length();
    while (len<=n){
        len*=2;
    }
    System.out.println(s.charAt((int)func(len,n)-1));
}
private static long func(long length,long pos){
    // 递推公式:n-1-l/2
    if (slen==length)
        return pos;
    long half = length/2;
    if (pos==half+1){
        // 后半段的第 1 个字符 -> 前半段的最后 1 个字符
        return func(half, half);
    }else if (pos<=half){
        return func(half,pos);
    }else{
        return func(half,pos-half-1);
    }
}

前缀和 & 差分

前缀和差分数组 是一对互逆运算:

  • 前缀和:用于快速 计算 某个范围 [L, R] 内的和
  • 差分数组:用于快速 修改 某个范围 [L, R] 内的值
posted @   RainNan  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示

目录导航