生活再平凡,也是限量版
总要热爱着什么,才不会被这无趣的生活吞没
若心有所向,平凡的日子也会泛着光
心怀浪漫宇宙,也珍惜人间日常
A:卡片
题目描述:
小蓝有很多数字卡片,每张卡片上都是数字 0 到 9。
小蓝准备用这些卡片来拼一些数,他想从 1 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。
小蓝想知道自己能从 11 拼到多少。
例如,当小蓝有 30 张卡片,其中 0 到 9 各 3 张,则小蓝可以拼出 1 到 10,
但是拼 11 时卡片 1 已经只有一张了,不够拼出 11。
现在小蓝手里有 0 到 9 的卡片各 2021 张,共 20210 张,请问小蓝可以从 1 拼到多少?
提示:建议使用计算机编程解决问题。
题解思路:
我们可以通过统计1---X花费每一种卡片的数量去判断是否能拼接X,如果不可以则输出的为X-1,如果可以则继续往下进行拼接
那么怎么样进行卡片使用的统计呢?
我们定义数组count[10] 分别用于表示0---9的使用量
然后每一次对X值每一位数进行判断,当count中有任意一位数超过2021时,结束
参考代码:
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
int []count=new int[10];
for(int i=1;;i++){
int t=i;
while(t!=0){//对当前值的每一位数进行判断
count[t%10]++;
if(count[t%10]>2021){//出现超过2021张卡片的时候
System.out.println(i-1);//输出
return;//强制结束
}
t/=10;
}
}
}
}
B:路径
题目描述:
小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图 中的最短路径。
小蓝的图由 2021 个结点组成,依次编号 1 至 2021。
对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点 之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条 长度为 a 和 b 的最小公倍数的无向边相连。
例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无 向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。
请计算,结点 1 和结点 2021 之间的最短路径长度是多少。
提示:建议使用计算机编程解决问题。
题解思路:
对于点 X,它可以通过 (x-1,x-2,x-3....x-21)获得,那么我们需要到达X点值最小,则对其取min即可,通过数学规律:a,b的最小公倍数=a*b/(a,b的最大公约数),同时,1与任何数的最小公倍数为该数本身,那么我们只需要从22开始到2021,每一次对能到达位置的值取最小即可
参考代码:
import java.io.*;
import java.util.Arrays;
public class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer st = new StreamTokenizer(br);
static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws Exception {
int []arr=new int[2022];
Arrays.fill(arr,1999999999);//直接赋值最大,然后去求最小(经常被骂,但是还是习惯这样写,呜呜呜)
for (int i=1;i<=22;i++ )arr[i]=i;//从1跳到x,最小公倍数为x,所以1---22的最小公倍数都是本体
for(int i=22;i<=2021;i++){//从22到2021
for(int j=1;j<=21;j++){//每一个i都可以从i-21至i-1过去
arr[i]=Math.min(arr[i],arr[i-j]+jl(i,i-j));//我们选择保留最小
}
}
pw.println(arr[2021]);
pw.flush();
}
public static int nextInt() throws Exception {//int型
st.nextToken();
return (int) st.nval;
}
public static long nextLong() throws Exception {//long型
st.nextToken();
return (long) st.nval;
}
public static int gcd(int a, int b ) {//求取最大公约数
return b !=0 ? gcd(b, a%b): a ;
}
public static int jl(int a, int b) {//求取最小公倍数
return a*b/gcd(a,b) ;
}
}
C:字符统计
题目描述:
给定一个只包含大写字母的字符串 SS, 请你输出其中出现次数最多的字符。
如果有多个字母均出现了最多次, 按字母表顺序依次输出所有这些字母。
题解思路:
与卡片类似,我们通过数组去存储每一个字符出现的次数,最终选取次数最多的字符,有多个就依次输出
因为A-Z总共有26个字符,所以数组长度至少需要26
通过 charAt(i)-‘A’去对字符转换成0---25,分别对应A---Z,在输出的时候记得转换回来
参考代码:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int []arr=new int[27];
String ss=scan.nextLine();//输入字符串
int sum=0;//记录最多出现的次数
for(int i=0;i<ss.length();i++) {
arr[ss.charAt(i)-'A'+1]++;//对应字符+1(个人采取1---26,也可以用0---25,看习惯)
sum=Math.max(sum,arr[ss.charAt(i)-'A'+1]);//更改出现最多次数
}
for(int i=0;i<27;i++){
if(arr[i]==sum)System.out.print((char)(i+'A'-1));//次数==最多次数即输出
}
}
}
D:费用报销
题目描述:
小明在出差结束后返回了公司所在的城市, 在填写差旅报销申请时, 粗心 的小明发现自己弄丢了出差过程中的票据。
为了弥补小明的损失, 公司同意小明用别的票据进行报销, 但是公司财务 要求小明提交的票据中任意两张的日期差不小于 K 天, 且总金额不得超过实际 差旅费用 M 。
比如财务要求 K=7 时, 若小明提交了一张 1 月 8 日的票据, 小明就不能 提交 1 月 2 日至 1 月 14 日之间的其他票据, 1 月 1 日及之前和 1 月 15 日及之 后的票据则可以提交。
公司的同事们一起给小明凑了 N 张票据, 小明现在想要请你帮他整理一 下, 从中选取出符合财务要求的票据, 并使总金额尽可能接近 M 。
需要注意, 由于这些票据都是同一年的, 因此 12 月底的票据不会影响到 1 月初票据的提交。这一年不是闰年。
输入格式:
第 1 行: 3 个整数, N,M,K
第 2…N+1 行: 每行 3 个整数 mi,di,vi, 第 i+1 行表示第 i 张票据时间 的月份 mi和日期 di,vi表示该票据的面值
题解思路:
PS:这题是01背包的变种问题,如果不会01背包的话建议先学01背包,不然看这篇会很头疼
我们可以吧日期统一起来,因为是在同一年内的,比如输入1 10 5,我们就看成10 5;输入是2 10 5,我们就看成(31+10) 5=41 5;这样子可以直接省去月份的干扰,同时方便后续的k值计算
我们通过排序,将日期在前的优先选择放入,后放入的和前面的进行对比差值是否小于k天,同时放入第i张票据时计算能满足的总金额数
最终在第n张票据放完以后,从右往左去找寻能满足的且小于M的最大金额,输出即可
参考代码:
import java.util.*;
import java.io.*;
public class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer st = new StreamTokenizer(br);
static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
static int []sj ={0,31,28,31,30,31,30,31,31,30,31,30,31};//时间
static int [][]arr = new int[1010][2];//存放输入
static boolean [][]pd=new boolean[1010][5010];//判断前i个能组成的m值
public static void main(String[] args) throws Exception {
int n=nextInt();
int m=nextInt();
int k=nextInt();
for(int i=1;i<=12;i++) sj[i]+=sj[i-1];
for(int i=1;i<=n;i++){
arr[i][0]=sj[nextInt()-1]+nextInt();//存放a月b日对应在一年中的第几天
arr[i][1]=nextInt();
}
Arrays.sort(arr, 1, n + 1, Comparator.comparingInt(a -> a[0]));//排序,按照时间排序
pd[0][0]=true;
int l=0;
for(int i=1;i<=n;i++){//n个商品,把第i个票据选择性放入
while (arr[i][0] - arr[l + 1][0] >= k) l++;//当前票据和前面已经可以放入的票据有多少不起冲突
for (int j = 0; j <= m; j++) {
pd[i][j] = pd[i - 1][j];//前面已经可以存在的价值,放入新的以后同样可以存在
if (j >= arr[i][1]&&pd[l][j - arr[i][1]]==true) pd[i][j]=true;//01模板样式
}
}
for (int i = m; i >= 0; i--) {
if (pd[n][i]) {
pw.println(i);
pw.flush();
return;
}
}
}
public static int nextInt() throws Exception {//int型
st.nextToken();
return (int) st.nval;
}
public static long nextLong() throws Exception {//long型
st.nextToken();
return (long) st.nval;
}
}