【数据结构与算法】(不完整版)

文章目录

    • @[toc]
  • **数据结构**
  • 1-1:线性表(数组、栈、队列、链表)
    • 栈:
        • 一:好串
        • 二:牛牛与后缀表达式
        • 三:栈和排序
        • 四:吐泡泡
    • 队列:
        • 一:Keep In Line
    • 链表:
  • 1-2:二叉树(遍历)
  • 1-3:集合(并查集、Hash表)
  • 1-4:图的基本应用(拓扑排序、遍历)
  • 2-1:二叉堆与树状数组
  • 2-2:线段树
  • **算法**
  • 1-1:模拟与高精度
    • 模拟
        • 一:[NOIP2015 普及组] 扫雷游戏
        • 二:[NOIP2016 提高组] 玩具谜题
        • 三:[1007] 魔法少女小Scarlet
        • 四:[NOIP2014 提高组] 生活大爆炸版石头剪刀布
        • 五:[NOIP2009 普及组] 多项式输出
        • 六:[NOIP2007 提高组] 字符串的展开
        • 七:[NOIP2010 提高组] 机器翻译
        • 八:小辰的圣剑
        • 九:小辰的借钱计划
        • 十:使三个字符串相等
        • 十一:区分黑球与白球
        • 十二:小红的01连续段
        • 十三:小红的01串构造
        • 十四:[NOIP2003 普及组] 乒乓球
        • 十五:[NOIP1998 提高组] 车站
    • 高精度:
        • 一:[NOIP1998 普及组] 阶乘之和
        • 二:阶乘数码
  • 1-2:排序
    • 自定义排序:
        • 一:[NOIP2007 普及组] 奖学金
        • 二:宇宙总统
        • 三:攀爬者
        • 四:生日
  • 1-3:枚举
    • 合适的枚举顺序:
        • 一:[NOIP2011 提高组] 铺地毯
        • 二:Number
    • 合适的枚举内容:
        • 一:[NOIP2016 普及组] 回文日期
        • 二:统计方形(数据加强版)
    • 枚举+高精度:
        • 一:最大异或乘积
    • 枚举+分治+数学:
        • 一:[NOIP1998 普及组] 幂次方
    • 枚举+模拟:
        • 一:[NOIP2014 普及组] 珠心算测验
  • 1-4:递推与递归
    • 斐波那契数列+高精度
        • 一:数楼梯
        • 二:蜜蜂路线
    • 记搜+动规
        • 一:[NOIP2002 普及组] 过河卒
        • 二:Function
    • 递推+递归+分治
        • 一:[NOIP2001 普及组] 数的计算
  • 1-5:贪心
        • 一:装进肚子
        • 二:小A的糖果
        • 三:凌乱的yyy / 线段覆盖
        • 四:排队接水
        • 五:[NOIP2012 提高组] 国王游戏
        • 六:华华听月月唱歌
        • 七:跳跳!
        • 八: [NOIP2007 普及组] 纪念品分组
        • 九: [USACO1.3] 混合牛奶 Mixing Milk
        • 十: 陶陶摘苹果(升级版)
        • 十一:Kevin逛超市 2
    • 贪心+高精度:
        • 一:删数问题
    • 贪心+优先队列
        • 一:[NOIP2004 提高组] 合并果子
  • 1-6:查找与二分答案
  • 1-7:搜索(BFS、DFS)
  • 2-1:前缀和、差分与离散化
      • 一维前缀和:
        • 一:火烧赤壁
      • 二维前缀和:
      • 最大子矩阵的和:
        • 一:最大加权矩形
        • 二:领地选择
      • 差分: b是a的差分数组 b的前缀和是a数组
        • 一:语文成绩
        • 二: [Poetize6] IncDec Sequence
  • 2-2:常见优化技巧(双指针、单调栈、单调队列、空间换时间)
  • 2-3:分治与倍增(ST表、快速幂)
  • 2-4:字符串(KMP、字典树)
  • 2-5:进阶搜索(状态剪枝)
  • **图论**
  • 2-1:树(最近公共祖先)
  • 2-2:单源最短路径(Dijkstra)
  • 2-3:最小生成树(Prim、Kruskal)
  • **数学**
  • 2-1:进阶数论(乘法逆元、质数筛、GCD、快速幂)
        • 一:质数筛
  • **动态规划**
  • DP1-1:背包问题
        • 一:小A点菜
  • 2-2:常见优化技巧(双指针、单调栈、单调队列、空间换时间)
  • 2-3:分治与倍增(ST表、快速幂)
  • 2-4:字符串(KMP、字典树)
  • 2-5:进阶搜索(状态剪枝)
  • **图论**
  • 2-1:树(最近公共祖先)
  • 2-2:单源最短路径(Dijkstra)
  • 2-3:最小生成树(Prim、Kruskal)
  • **数学**
  • 2-1:进阶数论(乘法逆元、质数筛、GCD、快速幂)
        • 一:质数筛
  • **动态规划**
  • DP1-1:背包问题
        • 一:小A点菜

数据结构

1-1:线性表(数组、栈、队列、链表)

栈:

先进后出(stack.peek():只获取栈顶元素 不弹出 stack.pol():获取栈顶元素 并且弹出)

一:好串

题目描述

牛牛喜欢跟字符串玩耍,他刚刚学会了一个新操作,将一个字符串x插入另一个字符串y中(包括放在开头和结尾)
牛牛认为如果一个串是好的当这个串能按照如下方法被构造出来:
一开始,有一个空串,然后执行0次或者若干次操作,每次操作将ab插入当前的字符串

根据上面的定义,ab, aabb, aababb都是好串,aab,ba,abbb并不是好串

现在给你一个字符串s,判断s是否是好串

输入描述:

输入一行包含一个字符串,长度不超过50

输出描述:

输出"Good" 或者 "Bad"

示例1

输入

ab

输出

Good

示例2

输入

aab

输出

Bad

示例3

输入

abaababababbaabbaaaabaababaabbabaaabbbbbbbb

输出

Bad
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        Stack<Character> stack = new Stack<>();
        boolean flag = true;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == 'a') {
                stack.push('a');
            } else {
                if (stack.isEmpty()) {
                    flag = false;
                    break;
                }
                stack.pop();
            }
        }
        if (flag) {
            if (stack.isEmpty()) {
                System.out.println("Good");
            } else {
                System.out.println("Bad");
            }
        } else {
            System.out.println("Bad");
        }
    }
}
二:牛牛与后缀表达式

题目描述

给定牛牛一个后缀表达式s,计算它的结果,例如,1+1对应的后缀表达式为1#1#+,‘#’作为操作数的结束符号。

其中,表达式中只含有‘+’、’-‘、’*‘三种运算,不包含除法。

本题保证表达式一定合法,且计算过程和计算结果的绝对值一定不会超过10^18
示例1

输入

"1#1#+"

返回值

2

说明

1#1#+这个后缀表达式表示的式子是1+1,结果为2 

示例2

输入

"12#3#+15#*"

返回值

225

说明

12#3#+15#*这个后缀表达式表示的式子是(12+3)*15,结果为225 
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定一个后缀表达式,返回它的结果
     * @param str string字符串 
     * @return long长整型
     */
    public long legalExp (String str) {
        // write code here
        Stack<Long> stack = new Stack<>();
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c >= '0' && c <= '9') {
                s.append(c);
            } else if (c == '#') {
                //来到操作数终止符
                //将#之前的字符拼接到一起压入栈
                stack.push(Long.valueOf(s.toString()));
                //重置s
                s = new StringBuilder();
            } else {
                //+   -   *
                long a = stack.pop();
                long b = stack.pop();
                if (c == '+') {
                    stack.push(a + b);
                } else if (c == '-') {
                    stack.push(-a + b);
                } else {
                    stack.push(a * b);
                }
            }
        }
        return stack.pop();
    }
}
三:栈和排序

题目描述

给你一个1->n的排列和一个栈,入栈顺序给定

你要在不打乱入栈顺序的情况下,对数组进行从大到小排序

当无法完全排序时,请输出字典序最大的出栈序列

输入描述:

第一行一个数n
第二行n个数,表示入栈的顺序,用空格隔开,结尾无空格

输出描述:

输出一行n个数表示答案,用空格隔开,结尾无空格

示例1

输入

5
2 1 5 3 4

输出

5 4 3 1 2

说明

2入栈;1入栈;5入栈;5出栈;3入栈;4入栈;4出栈;3出栈;1出栈;2出栈
import java.io.*;
import java.util.LinkedList;
import java.util.Scanner;

public class Main{
    public static void main(String[] args) throws IOException {
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] a = new int[n];
        int[] max = new int[n];//存入下一位置的最大值
        LinkedList<Integer> stack = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            a[i] = sc.nextInt();
        }
        //最后一位的最大值就是本身
        max[n - 1] = a[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            max[i] = Math.max(a[i], max[i + 1]);
        }
        for (int i = 0; i < n; i++) {
            stack.push(a[i]);
            //此时栈顶元素最大 比其他位置元素值都大 弹出
            while (!stack.isEmpty() && i < n - 1 && stack.peek() > max[i + 1]) {
                out.write(stack.pop()+" ");
            }
        }
        while (true) {
            if (stack.isEmpty()) {
                break;
            } else if (stack.size() == 1) {
                out.write(stack.pop()+"");
            } else {
                out.write(stack.pop()+" ");
            }
        }
        out.flush();
        out.close();
    }
}
四:吐泡泡

队列:

先进先出

一:Keep In Line

题目描述

又到饭点了,SK同学靠着惯性走到了食堂,但长长的队伍顿时让他失去了食欲。突然,他注意到某个窗口前的队伍里明显存在插队的现象,于是他默默记录下了同学们进队和出队的变化。

对于进队,SK同学只知道队伍里多了一个人,并不知道新来的人是老老实实站到了队尾还是插到了队伍里的某个位置;对于出队,SK同学能确定是队伍里站在最前面的人出队了。

初始时队伍为空,给出n条队伍进出的信息,保证已经出队的同学不会再入队,并且最终队伍也为空,现在SK同学想知道有多少不插队的好同学。

输入描述:

第一行是一个正整数T(≤ 5),表示测试数据的组数, 对于每组测试数据, 第一行是一个整数n(1≤ n ≤ 100000),表示这个队伍进出的信息数, 接下来n行,每行是两个字符串Opt Name,其中Opt为"in"代表进队,"out"代表出队,Name为进队或出队的人的名字, 所有信息按照时间顺序给出,名字由英文字母和阿拉伯数字组成,长度不超过10,保证每个人的名字各不相同。

输出描述:

对于每组测试数据,输出一行,包含一个整数,表示不插队的人数。

示例1

输入

1
6
in quailty
in hwq1352249
out hwq1352249
in zhuaiballl
out quailty
out zhuaiballl

输出

2
import com.sun.org.apache.xpath.internal.operations.Bool;

import java.util.*;

// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`,
// then press Enter. You can now see whitespace characters in your code.
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();
        while (T-- > 0) {
            int n = sc.nextInt();
            int count = 0;
            Map<String, Boolean> map = new HashMap<>();
            Queue<String> queue = new LinkedList<>();
            for (int i = 0; i < n; i++) {
                String opt = sc.next();
                String x = sc.next();
                if (opt.equals("in")) {
                    map.put(x, false);
                    queue.add(x);
                } else {
                    while (!queue.isEmpty() && map.get(queue.peek())) {
                        queue.poll();
                    }
                    if (x.equals(queue.peek())) {
                        count++;
                    }
                    map.put(x, true);
                }
            }
            System.out.println(count);
        }
    }
}

链表:

1-2:二叉树(遍历)

1-3:集合(并查集、Hash表)

1-4:图的基本应用(拓扑排序、遍历)

2-1:二叉堆与树状数组

2-2:线段树

算法

1-1:模拟与高精度

写模拟题时,遵循以下的建议有可能会提升做题速度:

在动手写代码之前,在草纸上尽可能地写好要实现的流程。

在代码中,尽量把每个部分模块化,写成函数、结构体或类。

对于一些可能重复用到的概念,可以统一转化,方便处理:如,某题给你 “YY-MM-DD 时:分” 把它抽取到一个函数,处理成秒,会减少概念混淆。

调试时分块调试。模块化的好处就是可以方便的单独调某一部分。

写代码的时候一定要思路清晰,不要想到什么写什么,要按照落在纸上的步骤写。

实际上,上述步骤在解决其它类型的题目时也是很有帮助的。


模拟

一:[NOIP2015 普及组] 扫雷游戏

题目描述

扫雷游戏是一款十分经典的单机小游戏。在 n 行 m 列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格)。玩家翻开一个非地雷格时,该格将会出现一个数字——提示周围格子中有多少个是地雷格。游戏的目标是在不翻出任何地雷格的条件下,找出所有的非地雷格。

现在给出 n 行 m 列的雷区中的地雷分布,要求计算出每个非地雷格周围的地雷格数。

注:一个格子的周围格子包括其上、下、左、右、左上、右上、左下、右下八个方向上与之直接相邻的格子。

输入格式

第一行是用一个空格隔开的两个整数 n 和 m,分别表示雷区的行数和列数。

接下来 n 行,每行 m 个字符,描述了雷区中的地雷分布情况。字符 * 表示相应格子是地雷格,字符 ? 表示相应格子是非地雷格。相邻字符之间无分隔符。

输出格式

输出文件包含 n 行,每行 m 个字符,描述整个雷区。用 * 表示地雷格,用周围的地雷个数表示非地雷格。相邻字符之间无分隔符。

样例 #1

样例输入 #1

3 3
*??
???
?*?

样例输出 #1

*10
221
1*1

样例 #2

样例输入 #2

2 3
?*?
*??

样例输出 #2

2*1
*21
import java.io.*;
import java.util.*;

public class Main {

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();//行
        int m = read.nextInt();//列
        String[] s = new String[n];//原地雷
        char[][] res = new char[n + 2][m + 2];//新地雷
        for (int i = 0; i < n; i++) {
            s[i] = read.next();
        }
        char[][] ans = new char[n][m];//原地雷的字符数组
        for (int i = 0; i < s.length; i++) {
            String s1 = s[i];
            char[] c = s1.toCharArray();
            for (int j = 0; j < c.length; j++) {
                ans[i][j] = c[j];
            }
        }
        for (int i = 0; i < res.length; i++) {
            for (int j = 0; j < res[0].length; j++) {
                res[i][j] = '0';
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (ans[i][j] == '*') {
                    res[i + 1][j + 1] = '*';
                }
            }
        }
        for (int i = 0; i < res.length; i++) {
            for (int j = 0; j < res[0].length; j++) {
                if (res[i][j] == '*') {
                    //左上
                    if (res[i - 1][j - 1] != '*') {
                        res[i - 1][j - 1] += 1;
                    }
                    //上
                    if (res[i - 1][j] != '*') {
                        res[i - 1][j] += 1;
                    }
                    //右上
                    if (res[i - 1][j + 1] != '*') {
                        res[i - 1][j + 1] += 1;
                    }
                    //左
                    if (res[i][j - 1] != '*') {
                        res[i][j - 1] += 1;
                    }
                    //右
                    if (res[i][j + 1] != '*') {
                        res[i][j + 1] += 1;
                    }
                    //左下
                    if (res[i + 1][j - 1] != '*') {
                        res[i + 1][j - 1] += 1;
                    }
                    //下
                    if (res[i + 1][j] != '*') {
                        res[i + 1][j] += 1;
                    }
                    //右下
                    if (res[i + 1][j + 1] != '*') {
                        res[i + 1][j + 1] += 1;
                    }
                }
            }
        }
        int cnt = 0;
        for (int i = 1; i < res.length - 1; i++) {
            cnt++;
            for (int j = 1; j < res[0].length - 1; j++) {
                if (cnt <= n) {
                    out.print(res[i][j]);
                }
            }
            out.println();
        }
        out.flush();
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
二:[NOIP2016 提高组] 玩具谜题

题目描述

小南有一套可爱的玩具小人, 它们各有不同的职业。

有一天, 这些玩具小人把小南的眼镜藏了起来。 小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外。如下图:

【数据结构与算法】(不完整版)_第1张图片

这时 singer 告诉小南一个谜題: “眼镜藏在我左数第 3 个玩具小人的右数第 1 个玩具小人的左数第 2 个玩具小人那里。 ”

小南发现, 这个谜题中玩具小人的朝向非常关键, 因为朝内和朝外的玩具小人的左右方向是相反的: 面朝圈内的玩具小人, 它的左边是顺时针方向, 右边是逆时针方向; 而面向圈外的玩具小人, 它的左边是逆时针方向, 右边是顺时针方向。

小南一边艰难地辨认着玩具小人, 一边数着:

singer 朝内, 左数第 3 个是 archer。

archer 朝外,右数第 1 个是 thinker 。

thinker 朝外, 左数第 2 个是 writer。

所以眼镜藏在 writer 这里!

虽然成功找回了眼镜, 但小南并没有放心。 如果下次有更多的玩具小人藏他的眼镜, 或是谜题的长度更长, 他可能就无法找到眼镜了。所以小南希望你写程序帮他解决类似的谜题。 这样的谜題具体可以描述为:

有 n 个玩具小人围成一圈, 已知它们的职业和朝向。现在第 1 个玩具小人告诉小南一个包含 m条指令的谜題, 其中第 z 条指令形如“左数/右数第 s,个玩具小人”。 你需要输出依次数完这些指令后,到达的玩具小人的职业。

输入格式

输入的第一行包含两个正整数 n,m,表示玩具小人的个数和指令的条数。

接下来 n 行,每行包含一个整数和一个字符串,以逆时针为顺序给出每个玩具小人的朝向和职业。其中 0 表示朝向圈内,1 表示朝向圈外。 保证不会出现其他的数。字符串长度不超过 10 且仅由英文字母构成,字符串不为空,并且字符串两两不同。整数和字符串之间用一个空格隔开。

接下来 m 行,其中第 i 行包含两个整数 ai,si,表示第 i 条指令。若 ai=0,表示向左数 si 个人;若 ai=1,表示向右数 si 个人。 保证 a i a_i ai 不会出现其他的数

输出格式

输出一个字符串,表示从第一个读入的小人开始,依次数完 m 条指令后到达的小人的职业。

样例 #1

样例输入 #1

7 3
0 singer
0 reader
0 mengbier 
1 thinker
1 archer
0 writer
1 mogician 
0 3
1 1
0 2

样例输出 #1

writer

样例 #2

样例输入 #2

10 10
1 C
0 r
0 P
1 d
1 e
1 m
1 t
1 y
1 u
0 V
1 7
1 1
1 4
0 5
0 3
0 1
1 6
1 2
0 8
0 4

样例输出 #2

y
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    static int place;//当前位置
    static int n, m;//n个小人   m条指令
    static int[] N;//存小人方向
    static String[] nam;//存小人名字

    //比较器
    public static class MyComoarator implements Comparator<Integer> {
        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }

    public static void main(String[] args) throws Exception {
        Read read = new Read();
        n = read.nextInt();//n个小人
        m = read.nextInt();//m条指令
        N = new int[n];//存小人方向
        nam = new String[n];//存小人名字
        for (int i = 0; i < n; i++) {
            N[i] = read.nextInt();//0:面向圈内   1:面向圈外
            nam[i] = read.next();//小人名字
        }
        for (int i = 0; i < m; i++) {
            f(read.nextInt(), read.nextInt());
        }
        out.println(nam[place]);
        out.flush();
    }

    // 其实只有两种情况,
    // 1 如果朝向内侧又向左移动表示为 0 0,如果朝向外侧有向右移动表示为1 1,这种情况下应该用当前位置减去移动的距离(减去小人数量)
    // 2 朝向和移动方向不想同0 1或者1 0,则应该用当前位置加上移动的距离(当前位置加上小人)
    public static void f(int i, int j) {
        if (N[place] == i) {
            place = (place+n - j) % n;
        } else {
            place = (place + j) % n;
        }
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
三:[1007] 魔法少女小Scarlet

题目描述

Scarlet 最近学会了一个数组魔法,她会在 n * n 二维数组上将一个奇数阶方阵按照顺时针或者逆时针旋转 90°。

首先,Scarlet 会把 1到 n^2 的正整数按照从左往右,从上至下的顺序填入初始的二维数组中,然后她会施放一些简易的魔法。

Scarlet 既不会什么分块特技,也不会什么 Splay 套 Splay,她现在提供给你她的魔法执行顺序,想让你来告诉她魔法按次执行完毕后的二维数组。

输入格式

第一行两个整数 n,m,表示方阵大小和魔法施放次数。

接下来 m 行,每行 4 个整数 x,y,r,z,表示在这次魔法中,Scarlet 会把以第 x 行第 y 列为中心的 2r+1 阶矩阵按照某种时针方向旋转,其中 z=0 表示顺时针,z=1 表示逆时针。

输出格式

输出 n 行,每行 n 个用空格隔开的数,表示最终所得的矩阵

样例 #1

样例输入 #1

5 4
2 2 1 0
3 3 1 1
4 4 1 0
3 3 2 1

样例输出 #1

5 10 3 18 15
4 19 8 17 20
1 14 23 24 25
6 9 2 7 22
11 12 13 16 21
import java.math.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();//n*n矩阵
        int m = sc.nextInt();//m次魔法次数
        int[][] ans = new int[n][n];//1~n*n
        int k = 0;
        for (int i = 0; i < ans.length; i++) {
            for (int j = 0; j < ans[0].length; j++) {
                ans[i][j] = ++k;
            }
        }
        for (int i = 0; i < m; i++) {
            //以(x,y)为中心的(2*r)+1阶矩阵
            int x = sc.nextInt();//2
            int y = sc.nextInt();//2
            int r = sc.nextInt();//1
            int z = sc.nextInt();//0:顺时针  1:逆时针
            int R = (2 * r) + 1;
            int[][] res = new int[R][R];//局部矩阵
            for (int j = x - r - 1, a = 0; j < x + r && a < ans.length; j++, a++) {
                for (int d = y - r - 1, b = 0; d < y + r && b < ans.length; d++, b++) {
                    res[a][b] = ans[j][d];
                }
            }
            if (z == 0) {//顺
                int[][] q = conversion(res);
                for (int j = x - r - 1, a = 0; j < x + r && a < q.length; j++, a++) {
                    for (int d = y - r - 1, b = 0; d < y + r && b < q.length; d++, b++) {
                        ans[j][d] = q[a][b];
                    }
                }
            } else {//逆
                int[][] q = f(res);
                for (int j = x - 1 - r, a = 0; j < x + r && a < q.length; j++, a++) {
                    for (int d = y - 1 - r, b = 0; d < y + r && b < q.length; d++, b++) {
                        ans[j][d] = q[a][b];
                    }
                }
            }
        }
        int cnt = 0;
        for (int i = 0; i < ans.length; i++) {
            cnt++;
            for (int j = 0; j < ans[0].length; j++) {
                if (cnt <= n) {
                    System.out.print(ans[i][j] + " ");
                }
            }
            System.out.println();
        }
    }

    //顺时针旋转矩阵
    public static int[][] conversion(int[][] array) {
        int row = array.length;
        int col = array[0].length;
        int result[][] = new int[row][col];
        int rrow = col;
        for (int i = 0; i < row; i++) {
            rrow -= 1;
            for (int j = 0; j < col; j++) {
                int temp = array[i][j];
                result[j][rrow] = temp;
            }
        }
        return result;
    }

    //逆时针
    public static int[][] f(int[][] array) {
        int row = array.length;
        int col = array[0].length;
        int result[][] = new int[row][col];
        int rrow = col;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                rrow -= 1;
                int temp = array[i][j];
                result[rrow][i] = temp;
            }
            rrow = col;
        }
        return result;
    }
}

四:[NOIP2014 提高组] 生活大爆炸版石头剪刀布

题目描述

石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头。如果两个人出拳一样,则不分胜负。在《生活大爆炸》第二季第 8 集中出现了一种石头剪刀布的升级版游戏。

升级版游戏在传统的石头剪刀布游戏的基础上,增加了两个新手势:

斯波克:《星际迷航》主角之一。

蜥蜴人:《星际迷航》中的反面角色。

这五种手势的胜负关系如表一所示,表中列出的是甲对乙的游戏结果。

【数据结构与算法】(不完整版)_第2张图片

现在,小 A 和小 B 尝试玩这种升级版的猜拳游戏。已知他们的出拳都是有周期性规律的,但周期长度不一定相等。例如:如果小 A 以“石头-布-石头-剪刀-蜥蜴人-斯波克”长度为 6 6 6 的周期出拳,那么他的出拳序列就是“石头-布-石头-剪刀-蜥蜴人-斯波克-石头-布-石头-剪刀-蜥蜴人-斯波克-…”,而如果小 B 以“剪刀-石头-布-斯波克-蜥蜴人”长度为 5 的周期出拳,那么他出拳的序列就是“剪刀-石头-布-斯波克-蜥蜴人-剪刀-石头-布-斯波克-蜥蜴人-…”

已知小 A 和小 B 一共进行 N N N 次猜拳。每一次赢的人得 1 分,输的得 0 分;平局两人都得 0 分。现请你统计 N 次猜拳结束之后两人的得分。

输入格式

第一行包含三个整数:N,NA,NB,分别表示共进行 N次猜拳、小 A 出拳的周期长度,小 B 出拳的周期长度。数与数之间以一个空格分隔。

第二行包含 NA 个整数,表示小 A 出拳的规律,第三行包含 NB 个整数,表示小 B 出拳的规律。其中,0 表示“剪刀”,1 表示“石头”,2 表示“布”,3 表示“蜥蜴人”,4 表示“斯波克”。数与数之间以一个空格分隔。

输出格式

输出一行,包含两个整数,以一个空格分隔,分别表示小 A、小 B 的得分。

样例 #1

样例输入 #1

10 5 6
0 1 2 3 4
0 3 4 2 1 0

样例输出 #1

6 2

样例 #2

样例输入 #2

9 5 5
0 1 2 3 4
1 0 3 2 4

样例输出 #2

4 4
import java.io.*;
import java.util.*;

public class Main {

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int N = read.nextInt();//猜拳10次
        int NA = read.nextInt();
        int NB = read.nextInt();
        List<Integer> a = new ArrayList<>();
        List<Integer> b = new ArrayList<>();
        List<Integer> lista = new ArrayList<>();
        List<Integer> listb = new ArrayList<>();
        for (int i = 0; i < NA; i++) {
            a.add(read.nextInt());
            lista.add(a.get(i));
        }
        for (int i = 0; i < NB; i++) {
            b.add(read.nextInt());
            listb.add(b.get(i));
        }
        if (NA < N && NB < N) {
            int t1 = N - NA;
            int t2 = N - NB;
            while (t1-- != 0) {
                for (int i = 0; i < a.size(); i++) {
                    lista.add(a.get(i));
                }
            }
            while (t2-- != 0) {
                for (int i = 0; i < b.size(); i++) {
                    listb.add(b.get(i));
                }
            }
        } else if (NA < N && NB > N) {
            int t = N - NA;
            while (t-- != 0) {
                for (int i = 0; i < a.size(); i++) {
                    lista.add(a.get(i));
                }
            }
        } else if (NA > N && NB < N) {
            int t = N - NB;
            while (t-- != 0) {
                for (int i = 0; i < b.size(); i++) {
                    listb.add(b.get(i));
                }
            }
        }
        int cnta = 0;
        int cntb = 0;
        for (int i = 0; i < N; i++) {
            if (lista.get(i) == 0) {//剪刀
                if (listb.get(i) == 2 || listb.get(i) == 3) {
                    cnta++;
                } else if (listb.get(i) == 1 || listb.get(i) == 4) {
                    cntb++;
                }
            }
            if (lista.get(i) == 1) {//石头
                if (listb.get(i) == 3 || listb.get(i) == 0) {
                    cnta++;
                } else if (listb.get(i) == 2 || listb.get(i) == 4) {
                    cntb++;
                }
            }
            if (lista.get(i) == 2) {//布
                if (listb.get(i) == 4 || listb.get(i) == 1) {
                    cnta++;
                } else if (listb.get(i) == 3 || listb.get(i) == 0) {
                    cntb++;
                }
            }
            if (lista.get(i) == 3) {//人
                if (listb.get(i) == 4 || listb.get(i) == 2) {
                    cnta++;
                } else if (listb.get(i) == 0 || listb.get(i) == 1) {
                    cntb++;
                }
            }
            if (lista.get(i) == 4) {//斯波克
                if (listb.get(i) == 0 || listb.get(i) == 1) {
                    cnta++;
                } else if (listb.get(i) == 2 || listb.get(i) == 3) {
                    cntb++;
                }
            }
        }
        out.print(cnta + " ");
        out.print(cntb);
        out.flush();
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
五:[NOIP2009 普及组] 多项式输出

题目描述

一元 n 次多项式

给出一个一元多项式各项的次数和系数,请按照如下规定的格式要求输出该多项式:

  1. 多项式中自变量为 x,从左到右按照次数递减顺序给出多项式。

  2. 多项式中只包含系数不为 0 的项。

  3. 如果多项式 n 次项系数为正,则多项式开头不出 + 号,如果多项式 n 次项系数为负,则多项式以 - 号开头。

  4. 对于不是最高次的项,以 + 号或者 - 号连接此项与前一项,分别表示此项系数为正或者系数为负。紧跟一个正整数,表示此项系数的绝对值(如果一个高于 0 次的项,其系数的绝对值为 1,则无需输出 1)。如果 x 的指数大于 1,则接下来紧跟的指数部分的形式为“x^b”,其中 b 为 x 的指数;如果 x 的指数为 1,则接下来紧跟的指数部分形式为 x;如果 x的指数为 0,则仅需输出系数即可。

  5. 多项式中,多项式的开头、结尾不含多余的空格。

输入格式

输入共有 2 行

第一行 1个整数,n,表示一元多项式的次数。

第二行有 n+1 个整数,其中第 i 个整数表示第 n-i+1 次项的系数,每两个整数之间用空格隔开。

输出格式

输出共 1 行,按题目所述格式输出多项式。

样例 #1

样例输入 #1

5 
100 -1 1 -3 0 10

样例输出 #1

100x^5-x^4+x^3-3x^2+10

样例 #2

样例输入 #2

3 
-50 0 0 1

样例输出 #2

-50x^3+1
import java.io.*;
import java.util.*;

public class Main {

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        for (int i = n; i >= 0; i--) {
            int a = read.nextInt();
            if (a == 0)
                continue;
            int abs = Math.abs(a);
            if (a < 0) {
                System.out.print("-");
            } else if (n != i) {
                System.out.print("+");
            }
            if (abs != 1) {
                System.out.print(abs);
            } else if (i == 0) {
                System.out.print(abs);
            }
            if (i > 0) {
                System.out.print("x");
            }
            if (i > 1) {
                System.out.print("^" + i);
            }
        }
        out.flush();
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
六:[NOIP2007 提高组] 字符串的展开

题目描述

在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于 d-h 或者 4-8 的字串,我们就把它当作一种简写,输出时,用连续递增的字母或数字串替代其中的减号,即,将上面两个子串分别输出为 defgh45678。在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活。具体约定如下:

(1) 遇到下面的情况需要做字符串的展开:在输入的字符串中,出现了减号 - ,减号两侧同为小写字母或同为数字,且按照 ASCII 码的顺序,减号右边的字符严格大于左边的字符。

(2) 参数 p1:展开方式。p1=1 时,对于字母子串,填充小写字母;p1=2 时,对于字母子串,填充大写字母。这两种情况下数字子串的填充方式相同。p1=3 时,不论是字母子串还是数字字串,都用与要填充的字母个数相同的星号 * 来填充。

(3) 参数 p2:填充字符的重复个数。p2=k 表示同一个字符要连续填充 k 个。例如,当 p2=3 时,子串d-h 应扩展为 deeefffgggh。减号两边的字符不变。

(4) 参数 p3:是否改为逆序:p3=1 表示维持原来顺序,p3=2 表示采用逆序输出,注意这时候仍然不包括减号两端的字符。例如当 p1=1、p2=2、p3=2 时,子串 d-h 应扩展为 dggffeeh

(5) 如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:d-e 应输出为 de3-4 应输出为 34。如果减号右边的字符按照 ASCII 码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:d-d 应输出为 d-d3-1 应输出为 3-1

输入格式

共两行。

第 1 行为用空格隔开的 3 个正整数,依次表示参数 p1,p2,p3。

第 2 行为一行字符串,仅由数字、小写字母和减号 - 组成。行首和行末均无空格。

输出格式

共一行,为展开后的字符串。

样例 #1

样例输入 #1

1 2 1
abcs-w1234-9s-4zz

样例输出 #1

abcsttuuvvw1234556677889s-4zz

样例 #2

样例输入 #2

2 3 2
a-d-d

样例输出 #2

aCCCBBBd-d
import java.io.*;
import java.util.*;

public class Main {

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int p1 = read.nextInt();
        int p2 = read.nextInt();
        int p3 = read.nextInt();
        String str = read.nextLine();
        StringBuilder sb = new StringBuilder();
        sb.append(str.charAt(0));
        int i;
        for (i = 1; i < str.length() - 1; i++) {
            if (str.charAt(i) == '-') {
                if (str.charAt(i - 1) >= '0' && str.charAt(i - 1) <= '9' && str.charAt(i + 1) >= '0' && str.charAt(i + 1) <= '9' && str.charAt(i + 1) > str.charAt(i - 1)) {
                    //-两边都是数字
                    if (str.charAt(i + 1) - str.charAt(i - 1) == 1) {
                        //-右边是左边的后继
                        continue;
                    } else {
                        if (p1 != 3) {
                            //不填*
                            if (p3 == 1) {
                                //原顺序
                                for (int j = str.charAt(i - 1) + 1; j < str.charAt(i + 1); j++) {
                                    char ch = (char) j;
                                    for (int k = 0; k < p2; k++) {
                                        sb.append(ch);
                                    }
                                }
                            } else if (p3 == 2) {
                                //逆序
                                for (int j = str.charAt(i + 1) - 1; j > str.charAt(i - 1); j--) {
                                    char ch = (char) j;
                                    for (int k = 0; k < p2; k++) {
                                        sb.append(ch);
                                    }
                                }
                            }
                        } else {
                            for (int j = str.charAt(i - 1) + 1; j < str.charAt(i + 1); j++) {
                                for (int k = 0; k < p2; k++) {
                                    sb.append("*");
                                }
                            }
                        }
                    }
                } else if (str.charAt(i - 1) >= 'a' && str.charAt(i - 1) <= 'z' && str.charAt(i + 1) >= 'a' && str.charAt(i + 1) <= 'z' && str.charAt(i - 1) < str.charAt(i + 1)) {
                    //-两边都是字母
                    if (str.charAt(i + 1) - str.charAt(i - 1) == 1) {
                        continue;
                    } else if (p1 != 3) {
                        int num = 0;
                        if (p1 == 1) {
                            //小写字母
                            num = 0;
                        } else if (p1 == 2) {
                            //大写字母
                            num = 32;
                        }
                        if (p3 == 1) {
                            //原顺序
                            for (int j = str.charAt(i - 1) + 1; j < str.charAt(i + 1); j++) {
                                char ch = (char) (j - num);
                                for (int k = 0; k < p2; k++) {
                                    //添加p2 个
                                    sb.append(ch);
                                }
                            }
                        } else {
                            //逆序
                            for (int j = str.charAt(i + 1) - 1; j > str.charAt(i - 1); j--) {
                                char ch = (char) (j - num);
                                for (int k = 0; k < p2; k++) {
                                    sb.append(ch);
                                }
                            }
                        }
                    } else {
                        //p1==3
                        for (int j = str.charAt(i - 1) + 1; j < str.charAt(i + 1); j++) {
                            for (int k = 0; k < p2; k++) {
                                //添加p2 个
                                sb.append("*");
                            }
                        }
                    }
                } else {
                    //两边类型不同
                    sb.append(str.charAt(i));
                }
            } else {
                //不是-直接添加
                sb.append(str.charAt(i));
            }
        }
        sb.append(str.charAt(i));
        out.println(sb);
        out.flush();
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
七:[NOIP2010 提高组] 机器翻译

题目描述

这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。

假设内存中有 M 个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过 M-1,软件会将新单词存入一个未使用的内存单元;若内存中已存入 M 个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。

假设一篇英语文章的长度为 N 个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。

输入格式

共 2 行。每行中两个数之间用一个空格隔开。

第一行为两个正整数 M,N,代表内存容量和文章的长度。

第二行为 N 个非负整数,按照文章的顺序,每个数(大小不超过 1000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。

输出格式

一个整数,为软件需要查词典的次数。

样例 #1

样例输入 #1

3 7
1 2 1 5 4 4 1

样例输出 #1

5
import java.io.*;
import java.util.*;

public class Main {

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int M = read.nextInt();
        int N = read.nextInt();
        int[] n = new int[N];
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            n[i] = read.nextInt();
        }
        list.add(n[0]);
        int cnt = 1;
        for (int i = 1; i < n.length; i++) {
            if (list.contains(n[i])) {
                continue;
            } else {
                if (list.size() >= M) {
                    list.remove(list.get(0));
                }
                list.add(n[i]);
                cnt++;
            }
        }
        out.println(cnt);
        out.flush();
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

八:小辰的圣剑

题目描述

小辰是异世界的勇士,小辰有一把耐久为 m的圣剑。有一天,魔法师预言未来 n 天中的第 i 天暗黑魔法师会召唤无数只血量为 aii 的怪物袭击王国,且第 i 天的怪物不会停留至第 i+1天。

圣剑每对怪物造成一点伤害都会损耗一点耐久,当圣剑的耐久消耗完后就不能对怪物造成伤害了。当怪物的血量为 0时,怪物就被小辰击杀了,如果第 i 天成功击杀了至少一只怪物,小辰在国民中的声望就会增加 bi,但是当小辰在国民中的声望超过 u 时,国王就会认为他的地位受到了威胁,从而囚禁小辰。

但是由于国民的记性很差,所以一旦有一天小辰没有出战,那么当天结束时小辰在国民中的声望就会降为 0。称小辰出战了,当且仅当当天小辰击杀了至少一只怪物。

但是小辰可以自主选择哪几天出战。小辰不想被囚禁自然不会想让自己在出战后的声望超过 u。

求小辰最多能连续出战多少天。

输入描述:

第一行三个整数 n (1≤n≤5000),m,u (1≤m,u≤5×1012)。

第二行 nnn 个整数表示 ai (1≤ai≤109)。

第三行 nnn 个整数表示 bi (−109≤bi≤109)。

输出描述:

输出一行一个整数表示小辰最多能连续出战多少天。

示例1

输入

4 7 10
2 3 4 5
6 7 8 9

输出

1
import java.io.*;
import java.util.*;

public class Main {

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();//天数
        long m = read.nextLong();//耐久
        long u = read.nextLong();//声望
        long[] a = new long[n];//blood
        long[] b = new long[n];//happy
        for (int i = 0; i < n; i++) {
            a[i] = read.nextLong();
        }
        for (int i = 0; i < n; i++) {
            b[i] = read.nextLong();
        }
        PPP[] p = new PPP[n];
        for (int i = 0; i < n; i++) {
            p[i] = new PPP(a[i], b[i]);
        }
        long cnt = 0;
        int j;
        //模拟+枚举
        //任意选一天 从第一天开始能连续多少天 第二天开始能连续多少天...(j-i):连续几天
        for (int i = 0; i < n; i++) {
            long mm = m;
            long uu = 0;
            //这一天下来宝剑的耐磨和声望
            for (j = i; j < n; j++) {
                mm -= p[j].blood;
                uu += p[j].happy;
                if (mm < 0 || uu > u) {
                    break;
                }
            }
            cnt = Math.max(cnt, j - i);
        }
        out.println(cnt);
        out.flush();
    }
}

class PPP {
    Long blood;
    long happy;

    public PPP(Long blood, Long happy) {
        this.blood = blood;
        this.happy = happy;
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

九:小辰的借钱计划

题目描述

​ 小辰有两个存钱的盒子,已知一个盒子中的钱数是另一个盒子中的钱数的倍数。现在小冰选择了其中的一个盒子,小辰告诉他这两个盒子里共有不超过 m 元,且小冰现在选的盒子里有 a 元,小冰期望要取出更多的钱,请问小冰是否应该更换选择的盒子。

​ 注:如果更换盒子取出钱数的期望比 a 大则应更换,反之不更换。且我们认为另一个盒子中各种钱数的概率是相等的。同时盒子里不会出现 0 元钱的情况。

多组测试数据。

输入描述:

第一行一个正整数 t (1≤t≤104)表示测试数据组数。

接下来每行两个正整数分别表示 m,a (1≤m,a≤100)

输出描述:

输出共 t 行。

每行一个字符串 YES 或 NO 表示结果。

示例1

输入

2
5 4
10 2

输出

NO
YES
import java.io.*;
import java.util.*;

public class Main {

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int t = read.nextInt();
        for (int i = 0; i < t; i++) {
            int m = read.nextInt();
            int a = read.nextInt();
            //另一个盒子最多的钱
            int b = m - a;


            double sum = 0;
            //如果sum(也就是另一个盒子的钱数)>a*倍数
            int count = 0;
            //枚举钱
            for (int j = 1; j <= b; j++) {
                //如果当前已知的数量是另一个盒子钱数的倍数
                if (a % j == 0 || j % a == 0) {
                    //另一个盒子的钱数
                    sum += j;
                    count++;
                }
            }
            if (sum > a * count) {
                out.println("YES");
            } else {
                out.println("NO");
            }
        }
        out.flush();
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

十:使三个字符串相等

给你三个字符串 s1s2s3。 你可以根据需要对这三个字符串执行以下操作 任意次数

在每次操作中,你可以选择其中一个长度至少为 2 的字符串 并删除其 最右位置上 的字符。

如果存在某种方法能够使这三个字符串相等,请返回使它们相等所需的 最小 操作次数;否则,返回 -1

示例 1:

输入:s1 = "abc",s2 = "abb",s3 = "ab"
输出:2
解释:对 s1 和 s2 进行一次操作后,可以得到三个相等的字符串。
可以证明,不可能用少于两次操作使它们相等。

示例 2:

输入:s1 = "dac",s2 = "bac",s3 = "cac"
输出:-1
解释:因为 s1 和 s2 的最左位置上的字母不相等,所以无论进行多少次操作,它们都不可能相等。因此答案是 -1 。

提示:

  • 1 <= s1.length, s2.length, s3.length <= 100
  • s1s2s3 仅由小写英文字母组成。
class Solution {
    public int findMinimumOperations(String s1, String s2, String s3) {
            if(s1.length()==1 && s2.length()==1&&s3.length()==1){
                if(s1.equals(s2) && s2.equals(s3)){
                    return 0;
                }
                return -1;
            }
        //字符串的最长公共前缀
        int ans=0;
        char[] a=s1.toCharArray();
        char[] b=s2.toCharArray();
        char[] c=s3.toCharArray();
        for(int i=0;i<1;i++){
            if(a[i]!=b[i] ||a[i]!=c[i]){
                return -1;
            }
        }
        int n=Math.min(s1.length(),Math.min(s2.length(),s3.length()));
        for(int i=0;i<n;i++){
            if(a[i]==b[i] && a[i]==c[i] && b[i]==c[i]){
                ans++;
            }else{
                break;
            }
        }
        //操作次数 s1.len-ans
        ans=(s1.length()+s2.length()+s3.length())-3*ans;
        return ans;
    }
}
十一:区分黑球与白球

桌子上有 n 个球,每个球的颜色不是黑色,就是白色。

给你一个长度为 n 、下标从 0 开始的二进制字符串 s,其中 10 分别代表黑色和白色的球。

在每一步中,你可以选择两个相邻的球并交换它们。

返回「将所有黑色球都移到右侧,所有白色球都移到左侧所需的 最小步数

示例 1:

输入:s = "101"
输出:1
解释:我们可以按以下方式将所有黑色球移到右侧:
- 交换 s[0] 和 s[1],s = "011"。
最开始,1 没有都在右侧,需要至少 1 步将其移到右侧。

示例 2:

输入:s = "100"
输出:2
解释:我们可以按以下方式将所有黑色球移到右侧:
- 交换 s[0] 和 s[1],s = "010"。
- 交换 s[1] 和 s[2],s = "001"。
可以证明所需的最小步数为 2 。

示例 3:

输入:s = "0111"
输出:0
解释:所有黑色球都已经在右侧。
class Solution {
    public long minimumSteps(String s) {
        //排成0000001111111

        //答案
        long ans=0;
        long cnt1=0;//统计每个0旁边有多少个1   每个0旁边有多少个1就要移动多少步累加
        for(int i=0;i<s.length();i++){
            char c=s.charAt(i);
            if(c=='0'){
                ans+=cnt1;
            }else{
                cnt1++;
            }
        }
        return ans;
    }
}
十二:小红的01连续段

题目描述

小红定义一个01串的“连续段”为:连续相同字符的极大长度。例如:"110001111"有一个长度为2的连续段,有一个长度为3的连续段,有一个长度为4的连续段。
小红拿到了一个01串,但其中有一些字符不可见了(用’?'表示)。小红想知道,这个01串的连续段长度的最大值最多能达到多少?

输入描述:

一个仅由'0'、'1'、'?'组成的字符串,长度不超过200000。

输出描述:

一个正整数,代表连续段长度的最大长度。

示例1

输入

1?0?1?

输出

3

说明

该字符串可以是"100011",最大的连续段长度为3。
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        int m0 = 0;
        int m1 = 0;
        int ans = 0;
        //模拟
        //遇到0 0个数增加 遇到1 1个数增加
        //遇到?  都增加
        //每次查询到这三个字符中其中一个  ans维护最大值
        for (char c : s.toCharArray()) {
            if (c == '0') {
                m0++;
                m1 = 0;
            } else if (c == '1') {
                m1++;
                m0 = 0;
            } else {
                m1++;
                m0++;
            }
            //维护区间内最大连续子串
            ans = Math.max(ans, Math.max(m0, m1));
        }
        System.out.println(ans);
    }
}


十三:小红的01串构造

题目描述

小红希望你构造一个长度为n的01串,其中恰好有k个1,且恰好有ttt对相邻字符都是1。你能帮帮她吗?

输入描述:

三个正整数n,k,t用空格隔开。

输出描述:

如果无法完成构造,请输出-1。否则输出任意一个满足条件01串即可。

示例1

输入

3 2 1

输出

110

说明

"110"为长度为3的01串,恰好有2个1,恰好有1对相邻数字是1。满足要求。
输出"011"也是可以通过本样例的。

示例2

输入

3 2 2

输出

-1

说明

两个'1'字符,显然无法构造出2对相邻的1。
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        int t = sc.nextInt();
        StringBuilder sb = new StringBuilder();
        //k占的长度  和  01占的长度相加 如果总长度n小于他俩的和   构建不了返回-1
        if (t >= k || n < k + (k - t - 1)) {
            System.out.println("-1");
        } else {
            // 首先排t对个1
            for (int i = 0; i <= t; i++) {
                sb.append("1");
            }
            //为什么补01?  前面t已经满足  接下来1不能连续出现 只能出现01 先拼接号字符串 最后在与n比较
            //补多少个01?  一共k个1    t对  此时需要k-t-1个01
            for (int i = t + 1; i < k; i++) {
                sb.append("01");
            }
            
            while (sb.length() < n) {
                sb.append(0);
            }
        }
        System.out.println(sb);
    }
}


十四:[NOIP2003 普及组] 乒乓球

题目背景

国际乒联现在主席沙拉拉自从上任以来就立志于推行一系列改革,以推动乒乓球运动在全球的普及。其中 11 分制改革引起了很大的争议,有一部分球员因为无法适应新规则只能选择退役。华华就是其中一位,他退役之后走上了乒乓球研究工作,意图弄明白 11 分制和 21 分制对选手的不同影响。在开展他的研究之前,他首先需要对他多年比赛的统计数据进行一些分析,所以需要你的帮忙。

题目描述

华华通过以下方式进行分析,首先将比赛每个球的胜负列成一张表,然后分别计算在 11 分制和 21 分制下,双方的比赛结果(截至记录末尾)。

比如现在有这么一份记录,(其中 W 表示华华获得一分,L 表示华华对手获得一分):

WWWWWWWWWWWWWWWWWWWWWWLW

在 11 分制下,此时比赛的结果是华华第一局 11 比 0 获胜,第二局 11 比 0 获胜,正在进行第三局,当前比分 1 比 1。而在 21 分制下,此时比赛结果是华华第一局 21比 0 获胜,正在进行第二局,比分 2 比 1。如果一局比赛刚开始,则此时比分为 0 比 。直到分差大于或者等于 2,才一局结束。

输入格式

每个输入文件包含若干行字符串,字符串有大写的 W 、 L 和 E 组成。其中 E 表示比赛信息结束,程序应该忽略 E 之后的所有内容。

输出格式

输出由两部分组成,每部分有若干行,每一行对应一局比赛的比分(按比赛信息输入顺序)。其中第一部分是 11 分制下的结果,第二部分是 21 分制下的结果,两部分之间由一个空行分隔。

样例 #1

样例输入 #1

WWWWWWWWWWWWWWWWWWWW
WWLWE

样例输出 #1

11:0
11:0
1:1

21:0
2:1


import java.io.BufferedWriter;
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
	Scanner sc=new Scanner(System.in);
	StringBuffer sb=new StringBuffer();//字符串拼接
	while(sc.hasNext()) {
	String s=sc.nextLine();
		sb.append(s);
		if(s.contains("E")) {
			break;
		}
	}
	char[] arr=sb.toString().toCharArray();
	f(arr,11);
	System.out.println();
	f(arr,21);
	
 }
public static void f(char arr[],int score) {
	int w=0;
	int l=0;
	for(int i=0;i<arr.length;i++) {
		if(arr[i] == 'W') {
			w++;
		}else if(arr[i] == 'L') {
			l++;
		}else if(arr[i] == 'E') {
			break;
		}
if((w>=score || l>=score) && (Math.abs(w-l)>=2)) {
			System.out.println(w+":"+l);
			w=0;
			l=0;			
		}
	}
	System.out.println(w+":"+l);
 }
}
十五:[NOIP1998 提高组] 车站

题目描述

火车从始发站(称为第 1 站)开出,在始发站上车的人数为 a,然后到达第 2 站,在第 2$站有人上、下车,但上、下车的人数相同,因此在第 2 站开出时(即在到达第 3 站之前)车上的人数保持为 a人。从第 3 站起(包括第 3 站)上、下车的人数有一定规律:上车的人数都是前两站上车人数之和,而下车人数等于上一站上车人数,一直到终点站的前一站(第 n-1 站),都满足此规律。现给出的条件是:共有 n 个车站,始发站上车的人数为 a,最后一站下车的人数是 m(全部下车)。试问 x 站开出时车上的人数是多少?

输入格式

输入只有一行四个整数,分别表示始发站上车人数 a,车站数 n,终点站下车人数 m 和所求的站点编号 x。

输出格式

输出一行一个整数表示答案:从 x 站开出时车上的人数。

样例 #1

样例输入 #1

5 7 32 4

样例输出 #1

13
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt();//始发站 车上a人
        int n = sc.nextInt();//一共n站
        int m = sc.nextInt();//最后一站下车人数
        int x = sc.nextInt();//求第x站上车人数
        int[] suma = new int[25];//a的系数
        int[] sumx = new int[25];//x的系数
        suma[2] = 1;
        suma[3] = 2;
        //思路:画图 按照车上人数列方程
        for (int i = 4; i < n; i++) {
            suma[i] = suma[i - 1] + suma[i - 2] - 1;
            sumx[i] = sumx[i - 1] + sumx[i - 2] + 1;
        }
        //(n-1)车上人数==m
        //车上人数:3*a+b*x
        int b = ((m - (a * suma[n - 1])) / sumx[n - 1]);
        System.out.println(a * suma[x] + b * sumx[x]);
    }
}

高精度:

一:[NOIP1998 普及组] 阶乘之和

题目描述

用高精度计算出 S = 1! + 2! + 3! + … + n!(n ~50)。

其中 ! 表示阶乘

输入格式

一个正整数 n。

输出格式

一个正整数 S,表示计算结果。

样例 #1

样例输入 #1

3

样例输出 #1

9
import java.io.*;
import java.math.*;
import java.util.*;

public class Main {

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        BigInteger bt = new BigInteger("1");
        BigInteger sum = new BigInteger("0");
        int n = read.nextInt();
        for (int i = 1; i <= n; i++) {
            BigInteger b = new BigInteger(String.valueOf(i));
            bt = bt.multiply(b);//阶乘的值
            sum = sum.add(bt);//阶乘的和
        }
        out.println(sum);
        out.flush();
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

二:阶乘数码

题目描述

求 n! 中某个数码出现的次数。

输入格式

第一行为 t(t <=10),表示数据组数。接下来 t 行,每行一个正整数 n(n<=1000) 和数码 a。

输出格式

对于每组数据,输出一个整数,表示 n! 中 a$出现的次数。

样例 #1

样例输入 #1

2
5 2
7 0

样例输出 #1

1
2
import java.math.BigInteger;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        //新建输入函数
        Scanner sc = new Scanner(System.in);
        int n, a, t;
        //计数器
        int cnt;
        //输入数据组数
        t = sc.nextInt();
        for (int i = 0; i < t; ++i) {
            //每一轮计数器归零
            cnt = 0;
            //输入正整数和数码
            n = sc.nextInt();
            a = sc.nextInt();
            //调用得到阶乘结果
            BigInteger res = factorial(BigInteger.valueOf(n));
            //计算每一位中a出现的个数
            //循环条件等价于 res != 0
            while (!res.equals(BigInteger.valueOf(0))) {
                //等价于 res % 10 == a
                //这里是调用了除法函数,返回的是数组:商和余数,所以索引是1
                if (res.divideAndRemainder(BigInteger.valueOf(10))[1].equals(BigInteger.valueOf(a))) {
                    cnt++;
                }
                //等价于 res = res / 10
                res = res.divide(BigInteger.valueOf(10));
            }
            //打印每一轮的结果
            System.out.println(cnt);
        }
    }

    //阶乘函数
    public static BigInteger factorial(BigInteger n) {
        //等价于 n == 1
        if (n.equals(BigInteger.valueOf(1))) {
            //等价于 return 1;
            return BigInteger.valueOf(1);
        }
        //等价于 n * factorial(n - 1)
        return n.multiply(factorial(n.subtract(BigInteger.valueOf(1))));
    }
}


1-2:排序

自定义排序:

一:[NOIP2007 普及组] 奖学金

题目描述

某小学最近得到了一笔赞助,打算拿出其中一部分为学习成绩优秀的前 5 名学生发奖学金。期末,每个学生都有 3 门课的成绩:语文、数学、英语。先按总分从高到低排序,如果两个同学总分相同,再按语文成绩从高到低排序,如果两个同学总分和语文成绩都相同,那么规定学号小的同学 排在前面,这样,每个学生的排序是唯一确定的。

任务:先根据输入的 3 门课的成绩计算总分,然后按上述规则排序,最后按排名顺序输出前五名名学生的学号和总分。注意,在前 5 名同学中,每个人的奖学金都不相同,因此,你必须严格按上述规则排序。例如,在某个正确答案中,如果前两行的输出数据(每行输出两个数:学号、总分) 是:

7 279
5 279

这两行数据的含义是:总分最高的两个同学的学号依次是 7 号、5 号。这两名同学的总分都是 279 (总分等于输入的语文、数学、英语三科成绩之和) ,但学号为 7 的学生语文成绩更高一些。如果你的前两名的输出数据是:

5 279
7 279

则按输出错误处理,不能得分。

输入格式

共 n+1行。

第 1 行为一个正整数n ( \le 300),表示该校参加评选的学生人数。

第 2 到 n+1 行,每行有 3 个用空格隔开的数字,每个数字都在 0到 100 之间。第 j 行的 3 个数字依次表示学号为 j-1 的学生的语文、数学、英语的成绩。每个学生的学号按照输入顺序编号为 1~n(恰好是输入数据的行号减 1)。

所给的数据都是正确的,不必检验。

输出格式

共 5 行,每行是两个用空格隔开的正整数,依次表示前 5 名学生的学号和总分。

样例 #1

样例输入 #1

6
90 67 80
87 66 91
78 89 91
88 99 77
67 89 64
78 89 98

样例输出 #1

6 265
4 264
3 258
2 244
1 237

样例 #2

样例输入 #2

8
80 89 89
88 98 78
90 67 80
87 66 91
78 89 91
88 99 77
67 89 64
78 89 98

样例输出 #2

8 265
2 264
6 264
1 258
5 258
import java.io.*;
import java.util.*;

public class Main {

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        Student[] stu = new Student[n];
        for (int i = 0; i < n; i++) {
            stu[i] = new Student(i + 1, read.nextInt(), read.nextInt(), read.nextInt());
        }
        //自定义排序
        //总成绩大先输出  若总成绩相同 则按照语文成绩大先输出 若都相同 再按照学号小的先输出
        Arrays.sort(stu, (stu1, stu2) -> {
            int sum = stu1.getSum().compareTo(stu2.getSum());
            if (sum != 0) {
                return sum;//谁大 返回谁
            }
            int Chi = stu1.Chinese.compareTo(stu2.Chinese);
            if (Chi != 0) {
                return Chi;//谁大 返回谁
            }
            return stu2.id.compareTo(stu1.id);//stu2.id.compareTo(stu1.id)<0  则stu20  则2>1
        });
        int count = 0;
        for (int i = stu.length - 1; i >= 0; i--) {//逆序输出
            if (count == 5) {
                return;
            }
            System.out.println(stu[i].id + " " + stu[i].getSum());
            count++;
        }
    }
}

class Student {
    Integer id;//学号
    Integer Chinese;//语文成绩
    Integer Math;//数学成绩
    Integer English;//英语成绩

    public Student(Integer id, Integer Chinese, Integer Math, Integer English) {
        this.id = id;
        this.Chinese = Chinese;
        this.Math = Math;
        this.English = English;
    }

    //三门成绩的和
    public Integer getSum() {
        return Chinese + Math + English;
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
二:宇宙总统

题目描述

地球历公元 6036 年,全宇宙准备竞选一个最贤能的人当总统,共有 n 个非凡拔尖的人竞选总统,现在票数已经统计完毕,请你算出谁能够当上总统。

输入格式

第一行为一个整数 n,代表竞选总统的人数。

接下来有 n 行,分别为第一个候选人到第 n 个候选人的票数。

输出格式

共两行,第一行是一个整数 m,为当上总统的人的号数。

第二行是当上总统的人的选票。

样例 #1

样例输入 #1

5
98765
12365
87954
1022356
985678

样例输出 #1

4
1022356
import java.math.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        Big[] b = new Big[n];
        for (int i = 0; i < n; i++) {
            b[i] = new Big(i + 1, sc.nextBigInteger());
        }
        //按照sum的从小到大排序
        Arrays.sort(b, Comparator.comparing(b2 -> b2.sum));
        System.out.println(b[b.length - 1].id);
        System.out.println(b[b.length - 1].sum);
    }
}

class Big {
    int id;
    BigInteger sum;

    public Big(int id, BigInteger sum) {
        this.id = id;
        this.sum = sum;
    }
}
三:攀爬者

题目背景

HKE 考完 GDOI 之后跟他的神犇小伙伴们一起去爬山。

题目描述

他在地形图上标记了 N 个点,每个点 Pi 都有一个坐标 (xi,yi,zi)。所有点对中,高度值 z 不会相等。HKE 准备从最低的点爬到最高的点,他的攀爬满足以下条件:

(1) 经过他标记的每一个点;

(2) 从第二个点开始,他经过的每一个点高度 z 都比上一个点高;

(3) HKE 会飞,他从一个点 Pi 爬到 Pj 的距离为两个点的欧几里得距离。即,根号下(x1-x2)2+(y1-y2)2+(z1-z2)^2

现在,HKE 希望你能求出他攀爬的总距离。

输入格式

第一行,一个整数 N N N 表示地图上的点数。

接下来 N N N 行,三个整数 x i , y i , z i x_i,y_i,z_i xi,yi,zi 表示第 i i i 个点的坐标。

输出格式

一个实数,表示 HKE 需要攀爬的总距离(保留三位小数)

样例 #1

样例输入 #1

5
2 2 2
1 1 1
4 4 4
3 3 3
5 5 5

样例输出 #1

6.928
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        Person[] per = new Person[n];
        for (int i = 0; i < per.length; i++) {
            per[i] = new Person(sc.nextInt(), sc.nextInt(), sc.nextInt());
        }
        Arrays.sort(per, Comparator.comparing(per1 -> per1.z));
        //第一座山的坐标
        double x = per[0].x;
        double y = per[0].y;
        double z = per[0].z;
        double ans = 0;//统计攀爬距离
        for (int i = 1; i < n; i++) {
            ans += Math.sqrt(Math.pow(per[i].x - x, 2) + Math.pow(per[i].y - y, 2) + Math.pow(per[i].z - z, 2));
            //计算各个山的攀爬距离
            //所以当前位置·要发生变动
            x=per[i].x;
            y=per[i].y;
            z=per[i].z;
        }
        System.out.printf("%.3f", ans);
    }
}

class Person {
    int x;
    int y;
    int z;

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


}
四:生日

题目描述

cjf 君想调查学校 OI 组每个同学的生日,并按照年龄从大到小的顺序排序。但 cjf 君最近作业很多,没有时间,所以请你帮她排序。

输入格式

输入共有 n + 1 行,

第 1 行为 OI 组总人数 n;

第 2 行至第 n+1 行分别是每人的姓名 s、出生年 y、月 m、日 d。

输出格式

输出共有 n 行,

即 n 个生日从大到小同学的姓名。(如果有两个同学生日相同,输入靠后的同学先输出)

样例 #1

样例输入 #1

3
Yangchu 1992 4 23
Qiujingya 1993 10 13
Luowen 1991 8 1

样例输出 #1

Luowen
Yangchu
Qiujingya
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        Person[] per = new Person[n];
        for (int i = 0; i < n; i++) {
            per[i] = new Person(sc.next(), sc.nextInt(), sc.nextInt(), sc.nextInt());
        }
        Arrays.sort(per, (per1, per2) -> {
            //如果年相同
            int year = per1.getYear() - per2.year;
            if (year != 0) {
                return year;
            }
            //如果月相同
            int mon = per1.getMonth() - per2.getMonth();
            if (mon != 0) {
                return mon;
            }
            //如果日相同
            int d = per1.getDay() - per2.getDay();
            if (d != 0) {
                return d;
            }
            //(年月日都相同)先输出后面的
            return per2.name.compareTo(per1.name);
        });
        for (int i = 0; i < per.length; i++) {
            System.out.println(per[i].name);
        }
    }
}

class Person {
    String name;
    int year;
    int month;
    int day;

    public Person(String name, int year, int month, int day) {
        this.name = name;
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public int getYear() {
        return year;
    }

    public int getMonth() {
        return month;
    }

    public int getDay() {
        return day;
    }

}

1-3:枚举

枚举的时候要想清楚:可能的情况是什么?要枚举哪些要素?减少枚举的空间枚举的范围是什么?是所有的内容都需要枚举吗?

在用枚举法解决问题的时候,一定要想清楚这两件事,否则会带来不必要的时间开销。

选择合适的枚举顺序根据题目判断。

思路:合适的枚举对象、顺序、方法

合适的枚举顺序:

一:[NOIP2011 提高组] 铺地毯

题目描述

为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标系的第一象限)铺上一些矩形地毯。一共有 n 张地毯,编号从 1 到 n。现在将这些地毯按照编号从小到大的顺序平行于坐标轴先后铺设,后铺的地毯覆盖在前面已经铺好的地毯之上。

地毯铺设完成后,组织者想知道覆盖地面某个点的最上面的那张地毯的编号。注意:在矩形地毯边界和四个顶点上的点也算被地毯覆盖。

输入格式

输入共 n + 2 行。

第一行,一个整数 n,表示总共有 n 张地毯。

接下来的 n 行中,第 i+1 行表示编号 i 的地毯的信息,包含四个整数 a ,b ,g ,k,每两个整数之间用一个空格隔开,分别表示铺设地毯的左下角的坐标 (a, b) 以及地毯在 x 轴和 y 轴方向的长度。

第 n + 2 行包含两个整数 x 和 y,表示所求的地面的点的坐标 (x, y)。

输出格式

输出共 1 行,一个整数,表示所求的地毯的编号;若此处没有被地毯覆盖则输出 -1

样例 #1

样例输入 #1

3
1 0 2 3
0 2 3 3
2 1 3 3
2 2

样例输出 #1

3

样例 #2

样例输入 #2

3
1 0 2 3
0 2 3 3
2 1 3 3
4 5

样例输出 #2

-1

提示

【样例解释 1】

如下图,1 号地毯用实线表示,2 号地毯用虚线表示,3 号用双实线表示,覆盖点 (2,2) 的最上面一张地毯是 3 号地毯。

【数据结构与算法】(不完整版)_第3张图片

import java.io.*;
import java.util.*;

public class Main {

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        int[][] res = new int[n][4];
        for (int i = 0; i < res.length; i++) {
            for (int j = 0; j < res[0].length; j++) {
                res[i][j] = read.nextInt();
            }
        }
        int x = read.nextInt();
        int y = read.nextInt();
        //从后往前枚举(因为后面的地毯会覆盖前面)
        for (int i = n - 1; i >= 0; i--) {
            //判断当前点是否在矩阵中
            //a<=x<=a+g  &&  b<=y<=b+k
            if ((x >= res[i][0] && x <= res[i][0] + res[i][2]) && (y >= res[i][1] && y <= res[i][1] + res[i][3])) {
                System.out.println(i + 1);
                return;
            }
        }
        System.out.println(-1);
        out.flush();
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

二:Number

题目描述

我们把帅数定义为素数平方和(平方), 素数立方体(立方), 和素数四次方(四次方).

帅帅的前四个数字是:

【数据结构与算法】(不完整版)_第4张图片

import com.sun.org.apache.xpath.internal.operations.Bool;

import java.util.*;

// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`,
// then press Enter. You can now see whitespace characters in your code.
public class Main {


    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long n = sc.nextLong();
        List<Long> list = new ArrayList<>();
        Set<Long> set = new HashSet<>();
        for (int i = 2; i <= Math.sqrt(n); i++) {
            if (f(i)) {
                list.add((long) i);
            }
        }
        for (int i = 0; i < list.size(); i++) {
            long a = list.get(i);
            long x = a * a * a * a;
            if (x > n) {
                break;
            }
            for (int j = 0; j < list.size(); j++) {
                long b = list.get(j);
                long y = b * b * b;
                if (x + y > n) {
                    break;
                }
                for (int k = 0; k < list.size(); k++) {
                    long c = list.get(k);
                    long z = c * c;
                    long num = x + y + z;
                    if (num > n) {
                        break;
                    } else {
                        set.add(num);
                    }
                }
            }
        }
        System.out.println(set.size());
    }
	//判断素数
    public static boolean f(int num) {
        boolean flag = true;
        for (int i = 2; i <= Math.sqrt(num); i++) {
            if (num % i == 0) {
                flag = false;
                break;
            }
        }
        return flag;
    }
}

合适的枚举内容:

一:[NOIP2016 普及组] 回文日期

题目描述

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

牛牛习惯用 8 位数字表示一个日期,其中,前 4 位代表年份,接下来 2 位代表月份,最后 2 位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表 示方法不会相同。

牛牛认为,一个日期是回文的,当且仅当表示这个日期的 8位数字是回文的。现在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存在的日期是回文的。

例如:

  • 对于 2016 年 11 月 19 日,用 8 位数字 20161119 表示,它不是回文的。
  • 对于 2010 年 1 月 2 日,用 8 位数字 20100102 表示,它是回文的。
  • 对于 2010 年 10 月 2 日,用 8 位数字 20101002 表示,它不是回文的。

每一年中都有 12 个月份:

其中,1, 3, 5, 7, 8, 10, 12 月每个月有 31天;4, 6, 9, 11 月每个月有 30 天;而对于 2 月,闰年时有 29 天,平年时有 28 天。

一个年份是闰年当且仅当它满足下列两种情况其中的一种:

  1. 这个年份是 4 的整数倍,但不是 100 的整数倍;
  2. 这个年份是 400 的整数倍。

例如:

  • 以下几个年份都是闰年:2000, 2012, 2016。
  • 以下几个年份是平年:1900, 2011, 2014。

输入格式

两行,每行包括一个 8 位数字。

第一行表示牛牛指定的起始日期。

第二行表示牛牛指定的终止日期。

输出格式

一个整数,表示在区间 之间,有多少个日期是回文的。

样例 #1

样例输入 #1

20110101
20111231

样例输出 #1

1

样例 #2

样例输入 #2

20000101
20101231

样例输出 #2

2
import java.io.*;
import java.util.*;

public class Main {

    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }


    public static void main(String[] args) throws Exception {
        //为什么不枚举年月日,只需要枚举月和日呢?
        //因为凑成凑成年月日的回文很少,只需要判断月日构成的回文是否在给定区间内
        //最终 通过月日的回文来推出年的回文

        Read read = new Read();
        String s1 = read.next();
        String s2 = read.next();
        int cnt = 0;
        //每个月的天数
        int[] days = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

        //枚举月份
        for (int i = 1; i <= 12; i++) {
            //枚举当前月份对应的天数
            for (int j = 1; j <= days[i]; j++) {
                StringBuilder sb = new StringBuilder();
                String month = String.valueOf(i);
                String day = String.valueOf(j);
                if (month.length() == 1) {
                    sb.append(0);
                }
                sb.append(i);
                if (day.length() == 1) {
                    sb.append(0);
                }
                sb.append(j);
                String sb2 = String.valueOf(sb);//月日
                sb.reverse();//月日反转
                sb.append(sb2);
                //如果此时月日构成回文
                if (sb.reverse().equals(sb)) {
                    int a1 = Integer.parseInt(s1);
                    int a2 = Integer.parseInt(s2);
                    int a3 = Integer.parseInt(String.valueOf(sb));
                    //在给定区间内查询即可
                    if (a3 >= a1 && a3 <= a2) {
                        cnt++;
                    }
                }
            }
        }
        out.println(cnt);
        out.flush();
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

二:统计方形(数据加强版)

题目描述

有一个 n X m 方格的棋盘,求其方格包含多少正方形、长方形(不包含正方形)。

输入格式

一行,两个正整数 n,m。

输出格式

一行,两个正整数,分别表示方格包含多少正方形、长方形(不包含正方形)。

样例 #1

样例输入 #1

2 3

样例输出 #1

8 10
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        long n = read.nextLong();
        long m = read.nextLong();
        long x = 0;//正方形个数
        long y = 0;//矩形个数
        for (long i = 1; i <= n; i++) {
            for (long j = 1; j <= m; j++) {
                x += Math.min(i, j);
                y += i * j;
            }
        }
        //矩形个数=正方形+长方形
        out.print(x + " " + (y - x));
        out.flush();
    }
}

class PPP {
    long x;
    long y;
    long z;

    public PPP(long x, long y, long z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

枚举+高精度:

一:最大异或乘积

给你三个整数 abn ,请你返回 (a XOR x) * (b XOR x)最大值x 需要满足 0 <= x < 2^n

由于答案可能会很大,返回它对 109 + 7 取余 后的结果。

注意XOR 是按位异或操作。

(<<):乘 (^):按位异或

示例 1:

输入:a = 12, b = 5, n = 4
输出:98
解释:当 x = 2 时,(a XOR x) = 14 且 (b XOR x) = 7 。所以,(a XOR x) * (b XOR x) = 98 。
98 是所有满足 0 <= x < 2n 中 (a XOR x) * (b XOR x) 的最大值。

示例 2:

输入:a = 6, b = 7 , n = 5
输出:930
解释:当 x = 25 时,(a XOR x) = 31 且 (b XOR x) = 30 。所以,(a XOR x) * (b XOR x) = 930 。
930 是所有满足 0 <= x < 2n 中 (a XOR x) * (b XOR x) 的最大值。

示例 3:

输入:a = 1, b = 6, n = 3
输出:12
解释: 当 x = 5 时,(a XOR x) = 4 且 (b XOR x) = 3 。所以,(a XOR x) * (b XOR x) = 12 。
12 是所有满足 0 <= x < 2n 中 (a XOR x) * (b XOR x) 的最大值。

提示:

  • 0 <= a, b < 250
  • 0 <= n <= 50
import java.math.*;
class Solution {
    public int maximumXorProduct(long a, long b, int n) {
        int mod = (int) (1e9 + 7);
         BigInteger big = new BigInteger(String.valueOf(a)).multiply(BigInteger.valueOf(b));
         for (int i = n - 1; i >= 0; i--) {
            long a1 = a ^ (1L << i);
            long b1 = b ^ (1L << i);
            BigInteger big2 = new BigInteger(String.valueOf(a1)).multiply(BigInteger.valueOf(b1));
            //subtract(减)  multiply(乘)   add(加)   devide(除)
            //如果big2更大
            if (big2.subtract(big).doubleValue() > 0) {
                a = a1;
                b = b1;
                big = big2;
            }
        }
        long a1 = a % mod;
        long b1 = b % mod;
        return (int) (a1 * b1 % mod);
    }   
}

枚举+分治+数学:

一:[NOIP1998 普及组] 幂次方

题目描述

任何一个正整数都可以用 2 的幂次方表示。例如 137=27+23+2^0。

同时约定次方用括号来表示,即 a^b 可表示为 a(b)。

由此可知,37 可表示为 2(7)+2(3)+2(0)

进一步:

7= 22+2+20 ( 2^1 用 2 表示),并且 3=2+2^0。

所以最后 137可表示为 2(2(2)+2+2(0))+2(2+2(0))+2(0)。

又如 1315=2^10 +2^8 +2^5 +2+1

所以 1315 最后可表示为 2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)。

输入格式

一行一个正整数 n。

输出格式

符合约定的 n的 0, 2 表示(在表示中不能有空格)。

样例 #1

样例输入 #1

1315

样例输出 #1

2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)
import java.util.*;

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

    public static void f(int n) {
        //枚举 2的14次方开始
        for (int i = 14; i >= 0; i--) {
            if (Math.pow(2, i) <= n) {
                if (i == 0) {   //2的0次分解为2(0)
                    System.out.print("2(0)");
                } else if (i == 1) { //2单独分解为2
                    System.out.print("2");
                } else {//i>1
                    System.out.print("2(");
                    f(i);
                    System.out.print(")");
                }
                n -= Math.pow(2, i);//继续循环分解余下的
                if (n != 0) {
                    System.out.print("+"); //加号处理的最简单方法:若此n还没分解完,则后面还有项,所以输出一个+号
                }
            }
        }
    }
}

枚举+模拟:

一:[NOIP2014 普及组] 珠心算测验

题目描述

某学校的珠心算老师采用一种快速考察珠心算加法能力的测验方法。他随机生成一个正整数集合,集合中的数各不相同,然后要求学生回答:其中有多少个数,恰好等于集合中另外两个(不同的)数之和?

最近老师出了一些测验题,请你帮忙求出答案。

输入格式

共两行,第一行包含一个整数 n,表示测试题中给出的正整数个数。

第二行有 n 个正整数,每两个正整数之间用一个空格隔开,表示测试题中给出的正整数。

输出格式

一个整数,表示测验题答案。

样例 #1

样例输入 #1

4
1 2 3 4

样例输出 #1

2
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparable<String>, Comparator<String> {

        @Override
        public int compareTo(String o) {
            return 0;
        }

        //字典序
        @Override
        public int compare(String a, String b) {
            return (a + b).compareTo(b + a);
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        int[] a = new int[n];
        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < n; i++) {
            a[i] = read.nextInt();
            set.add(a[i]);
        }
        int ans = 0;
        // 注意要输出数量而不是几种。。。
		//比如1+4=5与2+3=5是一种不是两种!!!!
        //所以数量为1
        for (int i = 0; i < a.length; i++) {
            for (int j = i + 1; j < a.length; j++) {
                //如果第三个数存在 方法++ 并且把第三个数删掉
                if (set.contains(a[i] + a[j])) {
                    ans++;
                    set.remove(a[i] + a[j]);
                }
            }
        }
        out.println(ans);
        out.flush();
    }
}


//log函数 base:以base为底
class Logarithm {
    static public double log(double value, double base) {
        return Math.log(value) / Math.log(base);
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

1-4:递推与递归

记忆化搜索是一种通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式。

因为记忆化搜索确保了每个状态只访问一次,它也是一种常见的动态规划实现方式。

  1. 什么是递归
  2. 如何给一堆数字排序?答:分成两半,先排左半边再排右半边,最后合并就行了,至于怎么排左边和右边,请重新阅读这句话。

递归的基本思想是某个函数直接或者间接地调用自身,这样原问题的求解就转换为了许多性质相同但是规模更小的子问题。求解时只需要关注如何把原问题划分成符合条件的子问题,而不需要过分关注这个子问题是如何被解决的。

斐波那契数列+高精度

一:数楼梯

题目描述

楼梯有 N 阶,上楼可以一步上一阶,也可以一步上二阶。

编一个程序,计算共有多少种不同的走法。

输入格式

一个数字,楼梯数。

输出格式

输出走的方式总数。

样例 #1

样例输入 #1

4

样例输出 #1

5
import java.io.*;
import java.math.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;
    static BigInteger[] a = new BigInteger[5005];

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        a[1] = BigInteger.valueOf(1);
        a[2] = BigInteger.valueOf(2);
        for (int i = 3; i <= n; i++) {
            a[i] = a[i - 1].add(a[i - 2]);
        }
        out.println(a[n]);
        out.flush();
    }

}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
二:蜜蜂路线

题目描述

一只蜜蜂在下图所示的数字蜂房上爬动,已知它只能从标号小的蜂房爬到标号大的相邻蜂房,现在问你:蜜蜂从蜂房 m 开始爬到蜂房 n,m n − 1 n-1 n1

【数据结构与算法】(不完整版)_第5张图片

输入格式

输入 m,n 的值

输出格式

爬行有多少种路线

样例 #1

样例输入 #1

1 14

样例输出 #1

377
import java.math.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();//从m开始
        int n = sc.nextInt();//到n
        //斐波那契数列
        BigInteger[] a = new BigInteger[n + 1];
        a[0] = BigInteger.valueOf(0);
        a[1] = BigInteger.valueOf(1);
        a[2] = BigInteger.valueOf(1);
        for (int i = 3; i <= n; i++) {
            a[i] = a[i - 1].add(a[i - 2]);
        }
        //求m~n的路线
        System.out.println(a[n - m + 1]);
    }
}

记搜+动规

一:[NOIP2002 普及组] 过河卒

题目描述

棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。

棋盘用坐标表示,A 点 (0, 0)、B 点 (n, m),同样马的位置坐标是需要给出的。

【数据结构与算法】(不完整版)_第6张图片

现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

输入格式

一行四个正整数,分别表示 B 点坐标和马的坐标。

输出格式

一个整数,表示所有的路径条数。

样例 #1

样例输入 #1

6 6 3 3

样例输出 #1

6
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;
    //马走日时 坐标变换
    static int[] dx = {2, 1, -1, -2, -2, -1, 1, 2};
    static int[] dy = {1, 2, 2, 1, -1, -2, -2, -1};

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        int m = read.nextInt();
        int x = read.nextInt();
        int y = read.nextInt();
        n += 1;
        m += 1;
        boolean[][] flag = new boolean[n][m];//标记不能走的点
        long[][] a = new long[n][m];
        flag[x][y] = true;
        for (int i = 0; i < 8; i++) {
            int nex = x + dx[i];
            int ney = y + dy[i];
            if (nex < 0 || ney >= a.length || ney < 0 || nex >= a[0].length) {
                continue;
            }
            //相当于马走日的各个点标记为true
            flag[nex][ney] = true;
        }
        for (int i = 0; i < n; i++) {
            //标记该行不能走
            if (flag[i][0]) {
                break;
            }
            //否则能走这条路方案数+1
            a[i][0] = 1;
        }
        for (int i = 0; i < m; i++) {
            //标记该列不能走
            if (flag[0][i]) {
                break;
            }
            //否则能走这条路方案数+1
            a[0][i] = 1;
        }
        for (int i = 1; i < n; i++) {
            for (int j = 1; j < m; j++) {
                if (flag[i][j]) {
                    continue;
                }
                //状态转移方程
                //到达(i,j)位置的方案数目只能是左边或者上边走过来
                a[i][j] = a[i - 1][j] + a[i][j - 1];
            }
        }
        out.println(a[n - 1][m - 1]);
        out.flush();
    }

}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
二:Function

题目描述

对于一个递归函数 w(a,b,c)

  • 如果 a < 0 或 b < 0 或 c < 0 就返回值 1。
  • 如果 a>20 或 b>20 或 c>20 就返回 w(20,20,20)
  • 如果 a
  • 其它的情况就返回 w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1)

这是个简单的递归函数,但实现起来可能会有些问题。当 a,b,c 均为 15 时,调用的次数将非常的多。你要想个办法才行。

注意:例如 w(30,-1,0) 又满足条件 1 又满足条件 2,请按照最上面的条件来算,答案为 1。

输入格式

会有若干行。

并以 -1,-1,-1 结束。

输出格式

输出若干行,每一行格式:

w(a, b, c) = ans

注意空格。

样例 #1

样例输入 #1

1 1 1
2 2 2
-1 -1 -1

样例输出 #1

w(1, 1, 1) = 2
w(2, 2, 2) = 4
import java.util.*;

public class Main {
    static long[][][] dp = new long[25][25][25];

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long a = sc.nextLong();
        long b = sc.nextLong();
        long c = sc.nextLong();
        while (a != -1 || b != -1 || c != -1) {
            System.out.println("w(" + a + ", " + b + ", " + c + ") = " + w(a, b, c));
            a = sc.nextLong();
            b = sc.nextLong();
            c = sc.nextLong();
        }
    }


    //记搜
    //开创一个数组 看当前状态是否被访问过
    //如果被访问过 直接返回
    //否则,添加此状态
    public static long w(long a, long b, long c) {
        if (a <= 0 || b <= 0 || c <= 0) {
            return 1;
        } else if (a > 20 || b > 20 || c > 20) {
            return w(20, 20, 20);
        } else if (dp[(int) a][(int) b][(int) c] != 0) {
            return dp[(int) a][(int) b][(int) c];
        }
        if (a < b && b < c) {
            dp[(int) a][(int) b][(int) c] = w(a, b, c - 1) + w(a, b - 1, c - 1) - w(a, b - 1, c);
        } else {
            dp[(int) a][(int) b][(int) c] = w(a - 1, b, c) + w(a - 1, b - 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1);
        }
        return dp[(int) a][(int) b][(int) c];
    }
}

递推+递归+分治

一:[NOIP2001 普及组] 数的计算

题目描述

给出正整数 n,要求按如下方式构造数列:

  1. 只有一个数字 n 的数列是一个合法的数列。
  2. 在一个合法的数列的末尾加入一个正整数,但是这个正整数不能超过该数列最后一项的一半,可以得到一个新的合法数列。

请你求出,一共有多少个合法的数列。两个合法数列 a, b 不同当且仅当两数列长度不同或存在一个正整数 i <=|a|,使得 ai ≠ bi。

输入格式

输入只有一行一个整数,表示 n。

输出格式

输出一行一个整数,表示合法的数列个数。

样例 #1

样例输入 #1

6

样例输出 #1

6

提示

样例 1 解释

满足条件的数列为:

  • 6
  • 6, 1
  • 6, 2
  • 6, 3
  • 6, 2, 1
  • 6, 3, 1
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparable<String>, Comparator<String> {

        @Override
        public int compareTo(String o) {
            return 0;
        }

        //字典序
        @Override
        public int compare(String a, String b) {
            return (a + b).compareTo(b + a);
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        int[] f = new int[n + 10];//存每一位数的种类
        //f(1)=1
        //f(2)=2=f(1)+1
        //f(3)=2=f(1)+1
        //f(4)=4=f(1)+f(2)+1
        //f(5)=4=f(1)+f(2)+1
        //f(6)=6=f(1)+f(2)+f(3)+1
        //+1都是在家本身
        for (int i = 1; i <= n; i++) {//1~n递推
            for (int j = 1; j <= i / 2; j++) {
                f[i] += f[j];//开始递推
            }
            f[i]++;//加上本身
        }
        out.println(f[n]);
        out.flush();
    }
}


//log函数 base:以base为底
class Logarithm {
    static public double log(double value, double base) {
        return Math.log(value) / Math.log(base);
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

1-5:贪心

一:装进肚子

题目描述

自从ZZZZone吃完糖果后,他开始改吃巧克力了,他每天想吃n个巧克力增在甜蜜值,他决定早上吃K个巧克力,晚上吃n - K个巧克力,每个巧克力在早上吃和在晚上吃的甜蜜值是不一样的,他想让自己得到的甜蜜值最大,并想知道最大是多少。

请你编程帮助他。

输入描述:

第一行包含两个数n,K表示每天要吃的巧克力数量和要在早上吃的数量。(n <= 100000, K <= n)
第二行包含n个整数Ai(1 <= i <= n) 表示个第i个巧克力在早上吃可得到的甜蜜值 (Ai <= 100000)
第三行包含n个整数Bi(1 <= i <= n) 表示个第i个巧克力在晚上吃可得到的甜蜜值 (Bi <= 100000)

输出描述:

输出仅一行包含一个整数表示ZZZZone能获得的最大甜蜜值。

示例1

输入

2 1
3 6
2 8

输出

11

说明

早上吃第一个巧克力得到3甜蜜值,晚上吃第2个巧克力得到8的甜蜜值,所以最大可得到11的甜蜜值。
import java.util.Arrays;
import java.util.Scanner;

public class Main {

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

        // 巧克力数量 、 早上吃的数量
        int n = in.nextInt();
        int k = in.nextInt();
        int[][] sweet = new int[2][n];
        int[] arr = new int[n];
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < n; j++) {
                sweet[i][j] = in.nextInt();
                if (i == 1) {
                    ans += sweet[i][j];
                    arr[j] = sweet[0][j] - sweet[1][j];
                }
            }
        }
        Arrays.sort(arr);
        for (int i = n - 1; i >= n - k; i--) {
            ans += arr[i];
        }

        System.out.println(ans);
    }
}


二:小A的糖果

小A的糖果

题目描述

小 A 有 n 个糖果盒,第 i 个盒中有 ai 颗糖果。

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

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

3 3
2 2 2

样例输出 #1

1

样例 #2

样例输入 #2

6 1
1 6 1 2 0 4

样例输出 #2

11

样例 #3

样例输入 #3

5 9
3 1 4 1 5

样例输出 #3

0

提示

样例输入输出 1 解释

吃掉第 2 盒中的一个糖果即可。

样例输入输出 2 解释

第 2 盒糖吃掉 6 颗,第 4 盒吃掉 2 颗,第 6 盒吃掉 3 颗。

import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        int x = read.nextInt();
        long[] a = new long[n];
        for (int i = 0; i < a.length; i++) {
            a[i] = read.nextLong();
        }
        //思路:第一盒和最后一盒能不吃就不吃,因为吃了就只分别影响后、前的一盒。但是吃一盒中间的就影响 前后两盒。
        //所以先判断第一盒,如果大于x,就吃到x为止,不要再贪吃了,ok
        //然后看第二盒让  第一盒+第二盒=x,第二盒为0,以此类推。
        //如果第一盒小于x  但是第一盒+第二盒 >x,则吃第二盒直到  第一盒+第二盒 == x。
        long ans = 0;
        //如果第一盒糖果大于x
        //吃到==x(sum+=a[0] - x)
        //此时第一盒==x(a[0] = x)
        if (a[0] > x) {
            ans += a[0] - x;
            a[0] = x;
        }


        //此时第一盒小于x
        //但是第一盒+第二盒大于x
        //让第一盒+第二盒吃到x为止
        //第二盒=x-第一盒
        for (int i = 1; i < n; i++) {
            if (a[i - 1] + a[i] > x) {
                ans += (a[i - 1] + a[i] - x);
                a[i] = x - a[i - 1];
            }
        }
        out.println(ans);
        out.flush();
    }
}


class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
三:凌乱的yyy / 线段覆盖

题目背景

快 noip 了,yyy 很紧张!

题目描述

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

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

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

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

输入格式

第一行是一个整数 n,接下来 n 行每行是 2 个整数 ai

输出格式

一个整数最多参加的比赛数目。

样例 #1

样例输入 #1

3
0 2
2 4
1 3

样例输出 #1

2
import java.io.*;
import java.util.*;

public class Main {
    static PPP[] p;
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }

	//代码没有AC 后面几个用例超内存
    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        p = new PPP[n];
        for (int i = 0; i < p.length; i++) {
            p[i] = new PPP(read.nextInt(), read.nextInt());
        }
        //按照结束时间早  从小到大排
        Arrays.sort(p, Comparator.comparing(p1 -> p1.end));
        int cnt = 1;
        int end = p[0].end;
        for (int i = 1; i < p.length; i++) {
            if (p[i].start >= end) {
                cnt++;
                end = p[i].end;
            }
        }
        out.println(cnt);
        out.flush();
    }
}

class PPP {
    int start;
    int end;

    public PPP(int start, int end) {
        this.start = start;
        this.end = end;
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
四:排队接水

题目描述

有 n 个人在一个水龙头前排队接水,假如每个人接水的时间为 Ti,请编程找出这 n 个人排队的一种顺序,使得 n 个人的平均等待时间最小。

输入格式

第一行为一个整数 n。

第二行 n个整数,第 i 个整数 Ti 表示第 i 个人的等待时间 Ti。

输出格式

输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。

样例 #1

样例输入 #1

10 
56 12 1 99 1000 234 33 55 99 812

样例输出 #1

3 2 7 8 1 4 9 6 10 5
291.90
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        People[] people = new People[n];
        for (int i = 0; i < n; i++) {
            people[i] = new People(i + 1, sc.nextInt());
        }
        //时间升序 若时间相同 编号升序
        Arrays.sort(people, (people1, people2) -> {
            int t = people1.time - people2.time;
            if (t != 0) {
                return t;
            }
            int x = people1.num - people2.num;
            if (x != 0) {
                return x;
            }
            return 0;
        });
        double sum = 0;

        //第一个人是不用排队的,所以没有等待时间,第一个人的接水时间为t1的话,
        //第二个人的等待时间为t1,第二个人的接水时间为t2的话,
        //第三个人的等待时间为t1+t2,
        //依次类推,最后一个人的等待时间为t1+t2+..+t(n-1),
        //将所有人的等待时间相加后,s=(n-1)t1+(n-2)t2+..+1*t(n-1)+0*tn;
        for (int i = 0; i < people.length; i++) {
            System.out.print(people[i].num + " ");
            sum += (n - i - 1) * people[i].time;
        }
        System.out.println();
        System.out.printf("%.2f", sum / n);
    }
}

class People {
    int num;
    int time;

    public People(int num, int time) {
        this.num = num;
        this.time = time;
    }
}
五:[NOIP2012 提高组] 国王游戏

题目描述

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

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

输入格式

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

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

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

输出格式

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

样例 #1

样例输入 #1

3 
1 1 
2 3 
7 4 
4 6

样例输出 #1

2
import java.io.*;
import java.math.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        int a1 = read.nextInt();
        int b1 = read.nextInt();
        people[] peo = new people[n];
        for (int i = 0; i < n; i++) {
            peo[i] = new people(read.nextInt(), read.nextInt());
        }
         //按照a*b排序
        //此时金币最大的大臣在最后
        //只需要计算国王和其他所有在他前面的大臣左手乘积除以最后一个大臣的右手即可
        Arrays.sort(peo, Comparator.comparing(peo1 -> peo1.a * peo1.b));
        BigInteger sum = new BigInteger(Integer.valueOf(a1).toString());
        for (int i = 0; i < peo.length - 1; i++) {
            sum = sum.multiply(BigInteger.valueOf(peo[i].a));
        }
        sum = sum.divide(BigInteger.valueOf(peo[peo.length - 1].b));
        if (sum.compareTo(BigInteger.valueOf(0)) == 0 && n > 1) {
            out.println("1");
        } else if (sum.compareTo(BigInteger.valueOf(0)) == 0 && n == 1) {
            out.println(0);
        } else {
            out.println(sum);
        }
        out.flush();
    }

}

class people {
    int a;
    int b;

    public people(int a, int b) {
        this.a = a;
        this.b = b;
    }
}


class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

六:华华听月月唱歌

题目描述

月月唱歌超级好听的说!华华听说月月在某个网站发布了自己唱的歌曲,于是把完整的歌曲下载到了U盘里。然而华华不小心把U盘摔了一下,里面的文件摔碎了。月月的歌曲可以看成由1到N的正整数依次排列构成的序列,它现在变成了若干个区间,这些区间可能互相重叠。华华想把它修复为完整的歌曲,也就是找到若干个片段,使他们的并集包含1到N(注意,本题中我们只关注整数,见样例1)。但是华华很懒,所以他想选择最少的区间。请你算出华华最少选择多少个区间。因为华华的U盘受损严重,所以有可能做不到,如果做不到请输出-1。

输入描述:

第一行两个正整数N、M,表示歌曲的原长和片段的个数。
接下来M行,每行两个正整数L、R表示第i的片段对应的区间是[L,R]。

输出描述:

如果可以做到,输出最少需要的片段的数量,否则输出-1。

示例1

输入

4 2
1 2
3 4

输出

2

示例2

输入

4 2
1 1
3 4

输出

-1

示例3

输入

10 5
1 1
2 5
3 6
4 9
8 10

输出

4
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int N = read.nextInt();
        int M = read.nextInt();
        PPP[] p = new PPP[M];
        for (int i = 0; i < M; i++) {
            p[i] = new PPP(read.nextLong(), read.nextLong());
        }
        //思路:左区间从小到大 记录区间最远能跳到哪(不能超过右+1)更新  若更新后依然没有到N 输出-1
        //左区间从小到大排序
        Arrays.sort(p, Comparator.comparing((p1 -> p1.L)));
        long ans = 0;//需要的片段数目
        long max = 0;//最大右端点
        long maxr = 0;//局部最大的右端点
        while (max < N) {
            //p[i].L <= max + 1  
            //例如2,5    3,6   4,9
            //2,5来到4,9而不是3,6
            //因为4、3都小与max+1 但是4来的更远 此时max=9
            for (int i = 0; i < M && p[i].L <= max + 1; i++) {
                maxr = Math.max(maxr, p[i].R);
            }
            //没跳出当前区间
            //即来到最后区间
            if (max == maxr) {
                break;
            }
            max = maxr;
            ans++;
        }
        if (max < N) {
            out.println(-1);
        } else {
            out.println(ans);
        }
        out.flush();
    }

}

class PPP {
    long L;
    long R;

    public PPP(long L, long R) {
        this.L = L;
        this.R = R;
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

七:跳跳!

题目描述

你是一只小跳蛙,你特别擅长在各种地方跳来跳去。

这一天,你和朋友小 F 一起出去玩耍的时候,遇到了一堆高矮不同的石头,其中第 i i i 块的石头高度为 hi,地面的高度是 h0 = 0。你估计着,从第 i 块石头跳到第 j 块石头上耗费的体力值为 (hi - hj) ^ 2,从地面跳到第 i 块石头耗费的体力值是 (hi) ^ 2。

为了给小 F 展现你超级跳的本领,你决定跳到每个石头上各一次,并最终停在任意一块石头上,并且小跳蛙想耗费尽可能多的体力值。

当然,你只是一只小跳蛙,你只会跳,不知道怎么跳才能让本领更充分地展现。

不过你有救啦!小 F 给你递来了一个写着 AK 的电脑,你可以使用计算机程序帮你解决这个问题,万能的计算机会告诉你怎么跳。

那就请你——会写代码的小跳蛙——写下这个程序,为你 NOIp AK 踏出坚实的一步吧!

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

2
2 1

样例输出 #1

5

样例 #2

样例输入 #2

3
6 3 5

样例输出 #2

49
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = read.nextInt();
        }
        //贪心策略:花费最大的力气
        //最大值与最小值交替
        Arrays.sort(a);//小
        List<Integer> list = new ArrayList<>();//大
        for (int i = a.length - 1; i >= 0; i--) {
            list.add(a[i]);
        }
        //此时a顺序 list逆序
        List<Integer> ans = new ArrayList<>();//长度为2n 后续遍历只需要n   交替存储 大小大小
        for (int i = 0; i < n; i++) {
            ans.add(list.get(i));
            ans.add(a[i]);
        }
        //地面跳到最高的力气
        long cnt = ans.get(0) * ans.get(0);
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j <= i + 1; j++) {
                cnt += (ans.get(i) - ans.get(j)) * (ans.get(i) - ans.get(j));
            }
        }
        out.println(cnt);
        out.flush();
    }

}


class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

八: [NOIP2007 普及组] 纪念品分组

题目描述

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

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

输入格式

共 n*+2 行:

第一行包括一个整数 w,为每组纪念品价格之和的上限。

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

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

输出格式

一个整数,即最少的分组数目。

输入输出样例

输入 #1

100 
9 
90 
20 
20 
30 
50 
60 
70 
80 
90

输出

6
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int w = read.nextInt();
        int n = read.nextInt();
        int[] a = new int[n];
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < a.length; i++) {
            a[i] = read.nextInt();
        }
        //排序
        //贪心策略:最大的和最小的组合 如果大于w 则集合添加大的,否则集合添加他们的和
        //相当于集合的长度就是答案
        Arrays.sort(a);
        int i = 0;
        for (int j = a.length - 1; j >= i; j--) {
            if (a[i] + a[j] > w) {
                list.add(a[j]);
            } else {
                list.add(a[i] + a[j]);
                i++;
            }
        }
        out.println(list.size());
        out.flush();
    }

}


class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

九: [USACO1.3] 混合牛奶 Mixing Milk

题目描述

由于乳制品产业利润很低,所以降低原材料(牛奶)价格就变得十分重要。帮助 Marry 乳业找到最优的牛奶采购方案。

Marry 乳业从一些奶农手中采购牛奶,并且每一位奶农为乳制品加工企业提供的价格可能相同。此外,就像每头奶牛每天只能挤出固定数量的奶,每位奶农每天能提供的牛奶数量是一定的。每天 Marry 乳业可以从奶农手中采购到小于或者等于奶农最大产量的整数数量的牛奶。

给出 Marry 乳业每天对牛奶的需求量,还有每位奶农提供的牛奶单价和产量。计算采购足够数量的牛奶所需的最小花费。

注:每天所有奶农的总产量大于 Marry 乳业的需求量。

输入格式

第一行二个整数 n,m,表示需要牛奶的总量,和提供牛奶的农民个数。

接下来 m 行,每行两个整数 pi,ai,表示第 i 个农民牛奶的单价,和农民 i 一天最多能卖出的牛奶量。

输出格式

单独的一行包含单独的一个整数,表示 Marry 的牛奶制造公司拿到所需的牛奶所要的最小费用。

样例 #1

样例输入 #1

100 5
5 20
9 40
3 10
8 80
6 30

样例输出 #1

630
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();//牛奶总量
        int m = read.nextInt();//m个人卖
        PPP[] p = new PPP[m];
        for (int i = 0; i < m; i++) {
            p[i] = new PPP(read.nextInt(), read.nextInt());
        }

        //贪心策略:花费最少的钱 买到n个牛奶
        // 按照单价从小到大排序
        Arrays.sort(p, Comparator.comparing(p1 -> p1.x));
        int ans = 0;
        for (int i = 0; i < p.length; i++) {
            if (n > p[i].y) {
                ans += p[i].x * p[i].y;
                n -= p[i].y;
            } else {
                ans += n * p[i].x;
                break;
            }
        }
        out.println(ans);
        out.flush();
    }

}

class PPP {
    int x;
    int y;

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

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
十: 陶陶摘苹果(升级版)

题目描述

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

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

输入格式

第 1 行:两个数 苹果数 n,力气 s。

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

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

输出格式

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

样例 #1

样例输入 #1

8 15
20 130
120 3
150 2
110 7
180 1
50 8
200 0
140 3
120 2

样例输出 #1

4
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();//果子数目
        int s = read.nextInt();//总力气
        int a = read.nextInt();//椅子高度
        int b = read.nextInt();//手最大长度
        PPP[] p = new PPP[n];
        for (int i = 0; i < n; i++) {
            			//苹果高度      力气
            p[i] = new PPP(read.nextInt(), read.nextInt());
        }
        //贪心策略:拿到更多的苹果  力气y从小到大排序
        Arrays.sort(p, Comparator.comparing(p1 -> p1.y));
        int ans = 0;
        for (int i = 0; i < p.length; i++) {
            if ((b >= p[i].x || b + a >= p[i].x) && s >= p[i].y) {
                ans++;
                s -= p[i].y;
            }
            if (s <= 0) {
                break;
            }
        }
        out.println(ans);
        out.flush();
    }
}

class PPP {
    int x;
    int y;

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

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
十一:Kevin逛超市 2

氧气少年在逛超市。

他总共买了 n 件商品,第 i 种商品的价格为 pi。

超市有下面的打折政策:

  • 每名顾客有 1 张折扣券,可以让一件商品的价格打折(如果此商品原价为pi,那么使用此优惠券后,价格变为 pi × x%。
  • 每名顾客有 1 张立减券,可以让一件商品的价格减小 y(如果此商品原价小于 y,那么可以花费 000 买下)。
  • 每个商品最多使用 1 张优惠券。

请求出氧气少年可能付出的最小的花费。

输入描述:

第一行包含一个整数 T(1≤T≤105),表示测试用例的组数。

对于每组测试用例:

第一行包含三个整数 n,x(1≤x≤99),y(1≤y≤104)
第二行包含 n 个整数 p1…pn表示商品的价格。

输出描述:

对于每组测试用例:
仅输出一行,包含一个实数,表示答案。如果你的答案和标准答案的绝对误差或相对误差不超过 10^-4,则你的答案会被视为正确。

示例1

输入

2
3 50 50
100 100 50
3 50 200
95 100 50

输出

150.000000000000
97.500000000000
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();
        for (int i = 0; i < T; i++) {
            int n = sc.nextInt();
            int x = sc.nextInt();
            int y = sc.nextInt();
            int[] p = new int[n];
            for (int j = 0; j < n; j++) {
                p[j] = sc.nextInt();
            }
            Arrays.sort(p);
            double x1 = (double) p[p.length - 1] * x / 100;//1折扣
            int y1 = (p[p.length - 1] - y);//1立减
            double x2 = (double) p[p.length - 2] * x / 100;//2折扣
            int y2 = (p[p.length - 2] - y);//2立减
            if (y1 < 0) {
                y1 = 0;
            }
            if (y2 < 0) {
                y2 = 0;
            }
            double sum = 0;
            for (int j = p.length - 3; j >= 0; j--) {
                sum += p[j];
            }
            sum += Math.min((x1 + y2), (x2 + y1));
            System.out.println(sum);
        }
    }
}

贪心+高精度:

一:删数问题

题目描述

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

输入格式

输入两行正整数。

第一行输入一个高精度的正整数 n。

第二行输入一个正整数 k,表示需要删除的数字个数。

输出格式

输出一个整数,最后剩下的最小数。

样例 #1

样例输入 #1

175438 
4

样例输出 #1

13
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        int k = sc.nextInt();
        char[] c = s.toCharArray();
        int i = 0;
        int t = k;
        //贪心策略:当前位置比后面位置大 则交换位置  小在前 大在后
        while (i < s.length() - 1 && t > 0) {
            if (c[i] > c[i + 1]) {
                for (int j = i; j < s.length() - 1; j++) {
                    c[j] = c[j + 1];
                }
                i = -1;
                t--;
            }
            i++;
        }
        //去除前导0
        int j = 0;
        while (c[j] == '0' && c.length - k-1 > 0) {
            j++;
        }
        for (int i1 = j; i1 < c.length - k; i1++) {
            System.out.print(c[i1]);
        }
    }
}

贪心+优先队列

一:[NOIP2004 提高组] 合并果子

题目描述

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

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

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

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

输入格式

共两行。
第一行是一个整数 n ,表示果子的种类数。

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

输出格式

一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2^31 。

样例 #1

样例输入 #1

3 
1 2 9

样例输出 #1

15
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;
    static char[] str = new char[100010];
    static int cnt = 0;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        //优先队列  默认小根堆 越前面越小
        //贪心策略:每次选择最小的   再放回去 再次选择最小的
        PriorityQueue<Integer> p = new PriorityQueue<>();
        for (int i = 0; i < n; i++) {
            p.add(read.nextInt());
        }
        int sum = 0;
        int a = 0;
        while (p.size() != 1) {
            a = p.poll() + p.poll();
            p.add(a);
            sum += a;
        }
        out.println(sum);
        out.flush();
    }

}


class Logarithm {

    static public double log(double value, double base) {

        return Math.log(value) / Math.log(base);

    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

1-6:查找与二分答案

1-7:搜索(BFS、DFS)

2-1:前缀和、差分与离散化

一维前缀和:

一:火烧赤壁

题目描述

给定每个起火部分的起点和终点,请你求出燃烧位置的长度之和。

输入格式

第一行一个整数,表示起火的信息条数 n。
接下来 n 行,每行两个整数 a, b,表示一个着火位置的起点和终点(注意:左闭右开)。

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

3
-1 1
5 11
2 9

样例输出 #1

11
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        PPP[] p = new PPP[n];
        for (int i = 0; i < n; i++) {
            p[i] = new PPP(read.nextLong(), read.nextLong());
        }
        Arrays.sort(p, Comparator.comparing(p1 -> p1.l));
        long l = p[0].l;
        long r = p[0].r;
        long ans = 0;
        //一共也就三种情况
        //当前区间与原区间没有重复
        //当前区间是原区间的子区间
        //当前区间与原区间有重叠部分
        

        //但是只比较当前左区间和原右区间
        //只有两种情况
        //1.当前区间与原区间没有重复
        //2.当前区间与原区间有重叠部分
        for (int i = 1; i < n; i++) {
            //第二种情况   维护右区间
            if (p[i].l < r) {
                r = Math.max(r, p[i].r);
            } else if (p[i].l > r) {//第一种情况
                ans += r - l;
                l = p[i].l;
                r = p[i].r;
            }
        }
        ans += r - l;
        out.println(ans);
        out.flush();
    }

}

class PPP {
    long l;
    long r;

    public PPP(long l, long r) {
        this.l = l;
        this.r = r;
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

二维前缀和:

public class Main {
    public static void main(String[] args) {
        Scanner read = new Scanner(System.in);
        int n = read.nextInt();
        int[][] a = new int[n + 1][n + 1];
        int[][] b = new int[n + 1][n + 1];//二维前缀和
        int max = Integer.MIN_VALUE;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                a[i][j] = read.nextInt();
            }
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + a[i][j];
            }
        }
    }
}

最大子矩阵的和:

package 模板;

import java.util.*;
/**
 *  n*n的矩阵
 *
 */
public class Main {
    public static void main(String[] args) {
        Scanner read = new Scanner(System.in);
        int n = read.nextInt();
        int[][] a = new int[n + 1][n + 1];
        int[][] b = new int[n + 1][n + 1];//二维前缀和
        int max = Integer.MIN_VALUE;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                a[i][j] = read.nextInt();
            }
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + a[i][j];
            }
        }
        //左上角坐标(x1,y1)
        //右下角坐标(x2,y2)
        //这个矩阵的所有数之和         b[x2][y2]-b[x2][y1]-b[x1][y2]+b[x1][y1]
        for (int x1 = 0; x1 <= n; x1++) {
            for (int y1 = 0; y1 <= n; y1++) {
                for (int x2 = x1; x2 <= n; x2++) {
                    for (int y2 = y1; y2 <= n; y2++) {
                        max = Math.max(max, b[x2][y2] - b[x2][y1] - b[x1][y2] + b[x1][y1]);
                    }
                }
            }
        }
        System.out.println(max);
    }
}

一:最大加权矩形

题目描述

为了更好的备战 NOIP2013,电脑组的几个女孩子 LYQ,ZSC,ZHQ 认为,我们不光需要机房,我们还需要运动,于是就决定找校长申请一块电脑组的课余运动场地,听说她们都是电脑组的高手,校长没有马上答应他们,而是先给她们出了一道数学题,并且告诉她们:你们能获得的运动场地的面积就是你们能找到的这个最大的数字。

校长先给他们一个 n*n 矩阵。要求矩阵中最大加权矩形,即矩阵的每一个元素都有一权值,权值定义在整数集上。从中找一矩形,矩形大小无限制,是其中包含的所有元素的和最大 。矩阵的每个元素属于 [-127,127] ,例如

 0 –2 –7  0 
 9  2 –6  2
-4  1 –4  1 
-1  8  0 –2

在左下角:

9  2
-4  1
-1  8

和为 15。

几个女孩子有点犯难了,于是就找到了电脑组精打细算的 HZH,TZY 小朋友帮忙计算,但是遗憾的是他们的答案都不一样,涉及土地的事情我们可不能含糊,你能帮忙计算出校长所给的矩形中加权和最大的矩形吗?

输入格式

第一行:n,接下来是 n 行 n 列的矩阵。

输出格式

最大矩形(子矩阵)的和。

样例 #1

样例输入 #1

4
0 -2 -7 0
 9 2 -6 2
-4 1 -4  1 
-1 8  0 -2

样例输出 #1

15
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        int[][] a = new int[n + 1][n + 1];
        int[][] b = new int[n + 1][n + 1];//二维前缀和
        int max = Integer.MIN_VALUE;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                a[i][j] = read.nextInt();
            }
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + a[i][j];
            }
        }
        //左上角坐标(x1,y1)
        //右下角坐标(x2,y2)
        //这个矩阵的所有数之和         b[x2][y2]-b[x2][y1]-b[x1][y2]+b[x1][y1]
        for (int x1 = 0; x1 <= n; x1++) {
            for (int y1 = 0; y1 <= n; y1++) {
                for (int x2 = x1; x2 <= n; x2++) {
                    for (int y2 = y1; y2 <= n; y2++) {
                        max = Math.max(max, b[x2][y2] - b[x2][y1] - b[x1][y2] + b[x1][y1]);
                    }
                }
            }
        }
        out.println(max);
        out.flush();
    }

}

class PPP {
    int m;
    int v;

    public PPP(int m, int v) {
        this.m = m;
        this.v = v;
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

二:领地选择

题目描述

作为在虚拟世界里统帅千军万马的领袖,小 Z 认为天时、地利、人和三者是缺一不可的,所以,谨慎地选择首都的位置对于小 Z 来说是非常重要的。

首都被认为是一个占地 C*C 的正方形。小 Z 希望你寻找到一个合适的位置,使得首都所占领的位置的土地价值和最高。

输入格式

第一行三个整数 N,M,C,表示地图的宽和长以及首都的边长。

接下来 N 行每行 M 个整数,表示了地图上每个地块的价值。价值可能为负数。

输出格式

一行两个整数 X,Y,表示首都左上角的坐标。

样例 #1

样例输入 #1

3 4 2
1 2 3 1
-1 9 0 2
2 0 1 1

样例输出 #1

1 2
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        int m = read.nextInt();
        int c = read.nextInt();
        int[][] a = new int[n + 1][m + 1];
        int[][] b = new int[n + 1][m + 1];//二维前缀和
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                a[i][j] = read.nextInt();
                b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + a[i][j];
            }
        }
        long max = Long.MIN_VALUE;
        int x = 0;
        int y = 0;

        //假设(c,c)是最大  即右下角坐标
        //但是求左上角坐标  即i-c+1
        for (int i = c; i <= n; i++) {
            for (int j = c; j <= m; j++) {
                long ms = b[i][j] - b[i - c][j] - b[i][j - c] + b[i - c][j - c];
                if (ms > max) {
                    x = i - c + 1;
                    y = j - c + 1;
                    max = ms;
                }
            }
        }
        out.print(x + " " + y);
        out.flush();
    }

}

class PPP {
    long l;
    long r;

    public PPP(long l, long r) {
        this.l = l;
        this.r = r;
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

差分: b是a的差分数组 b的前缀和是a数组

public class Main {
    public static void main(String[] args) {
        Scanner read = new Scanner(System.in);
        int n = read.nextInt();
        int[] a = new int[n];
        int[] b = new int[n];//差分数组
        for (int i = 0; i < n; i++) {
            a[i] = read.nextInt();
            if(i==0){
                b[i]=a[i];
            }else{
                b[i]=a[i]-a[i-1];
            }
        }
    }
}
一:语文成绩

题目描述

语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行。她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少。你能帮帮她吗?

输入格式

第一行有两个整数 n,p,代表学生数与增加分数的次数。

第二行有 n 个数,a1~an,代表各个学生的初始成绩。

接下来 p 行,每行有三个数,x,y,z,代表给第 x 个到第 y 个学生每人增加 z 分。

输出格式

输出仅一行,代表更改分数后,全班的最低分。

样例 #1

样例输入 #1

3 2
1 1 1
1 2 1
2 3 1

样例输出 #1

2
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        int p = read.nextInt();
        int[] a = new int[n];
        int[] b = new int[n];//差分数组
        for (int i = 0; i < n; i++) {
            a[i] = read.nextInt();
            if (i == 0) {
                b[i] = a[i];
            } else {
                b[i] = a[i] - a[i - 1];
            }
        }

        for (int i = 0; i < p; i++) {
            int x = read.nextInt() - 1;
            int y = read.nextInt() - 1;
            int z = read.nextInt();
            b[x] += z;//后面都加上z了 但是y后面的不加
            if (y + 1 < n) {
                b[y + 1] -= z;//后面不加
            }
        }
        int min = b[0];
        for (int i = 1; i < n; i++) {
            //b是a的差分数组  b的前缀和是a数组
            b[i] += b[i - 1];
            min = Math.min(min, b[i]);
        }
        out.println(min);
        out.flush();
    }

}

class PPP {
    long x;
    long y;
    long z;

    public PPP(long x, long y, long z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}
二: [Poetize6] IncDec Sequence

题目描述

给定一个长度为 n 的数列,每次可以选择一个区间[l,r],使这个区间内的数都加 1 或者都减 1。

请问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。

输入格式

第一行一个正整数 n
接下来 n 行,每行一个整数,第 i+1 行的整数表示 ai。

输出格式

第一行输出最少操作次数
第二行输出最终能得到多少种结果

样例 #1

样例输入 #1

4
1
1
2
2

样例输出 #1

1
2
import java.io.*;
import java.util.*;

public class Main {
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    static long mod = 95465484515L;

    //比较器
    public static class MyComoarator implements Comparator<Integer> {

        //o1-o2:从小到大
        //o2-o1从大到小
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }


    public static void main(String[] args) throws Exception {
        Read read = new Read();
        int n = read.nextInt();
        long[] a = new long[n];//长度为n的数列
        long p = 0;//增加操作
        long q = 0;//减少操作
        for (int i = 0; i < n; i++) {
            a[i] = read.nextLong();
        }
        for (int i = 1; i < n; i++) {
            long c = a[i] - a[i - 1]; // 计算相邻元素之间的差值
            if (c > 0) {//如果差值大于 0,说明需要增加操作
                p += c;
            } else {
                q -= c;
            }
        }
        //找到增加和减少操作次数中的较大值,作为最少操作次数
        //计算操作次数之差的绝对值加 1,作为最终可能的结果种数
        out.println(Math.max(p, q));
        out.println(Math.abs(p - q) + 1);
        out.flush();
    }
}

class PPP {
    long x;
    long y;
    long z;

    public PPP(long x, long y, long z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

class Read {
    BufferedReader bf;
    StringTokenizer st;
    BufferedWriter bw;

    public Read() {
        bf = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer("");
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

    }


    public String nextLine() throws IOException {
        return bf.readLine();
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()) {
            st = new StringTokenizer(bf.readLine());
        }
        return st.nextToken();
    }

    public char nextChar() throws IOException {
        //确定下一个token只有一个字符的时候再用
        return next().charAt(0);
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

}

2-2:常见优化技巧(双指针、单调栈、单调队列、空间换时间)

2-3:分治与倍增(ST表、快速幂)

2-4:字符串(KMP、字典树)

2-5:进阶搜索(状态剪枝)

图论

2-1:树(最近公共祖先)

2-2:单源最短路径(Dijkstra)

2-3:最小生成树(Prim、Kruskal)

数学

2-1:进阶数论(乘法逆元、质数筛、GCD、快速幂)

一:质数筛

题目描述

输入 n 个不大于 10^5 的正整数。要求全部储存在数组中,去除掉不是质数的数字,依次输出剩余的质数。

输入格式

第一行输入一个正整数 n,表示整数个数。

第二行输入 n 个正整数 ai,以空格隔开。

输出格式

输出一行,依次输出 ai 中剩余的质数,以空格隔开。

样例 #1

样例输入 #1

5
3 4 5 6 7

样例输出 #1

3 5 7
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = sc.nextInt();
            if (check(a[i])) {
                System.out.print(a[i] + " ");
            }
        }
    }

    public static boolean check(long n) {
        boolean flag = false;
        if 

你可能感兴趣的:(算法,java)