幸运的3
题目
你有 n 个数,可以将它们两两匹配(即将两数首尾相连),每个数只能使用一次,问匹配后最多有多少个 3 的倍数(没有进行匹配的数不算)?
格式
输入格式:第一行一个 n ,接下来 n 个正整数。
输出格式:输出匹配后最多有多少个 3 的倍数。
取值范围:1 ≤ n ≤ 10000, 1 ≤ x ≤ 100000000
样例
输入:3
123 123 99
输出:1
问题分析
要解决这个问题,我们首先需要理解如何判断一个数是否是 3 的倍数。一个数是 3 的倍数当且仅当其各位数字之和是 3 的倍数。因此,我们可以通过计算每个数的各位数字之和来分类这些数,然后尝试最大化匹配后得到的 3 的倍数的数量。
将所有的数分为三类:各位数字之和为 3 的倍数的数(即对 3 取余余数为 0 );各位数字之和对 3 取余余数为 1 的数;各位数字之和对 3 取余余数为 2 的数。我们可以初始化一个一维数组来存储三类数的个数。
很容易想到 3 的倍数只有再加上 3 的倍数才能又成为一个 3 的倍数,又因为没有进行匹配的数不算,因此当这类数的个数为偶数时,它匹配完恰好为原来的一半;当这类数的个数为奇数时,匹配完能凑成原来的个数减一的一半。我们利用向下整除就能恰好囊括这两种情况。
而余数为 1 的就只能与余数为 2 的匹配才能凑成一个 3 的倍数的数,其余则不能凑成功,因此我们只要得到两者间的最小值,就能找到这种情况能凑成的个数。
最后,相加起来这两种情况得到的个数,就是总个数了。
代码
import java.util.Scanner;
import java.util.*;
class Main {
// 存储余数为 0, 1, 2各有几个数字
static int[] arr = new int[3];
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
// 总个数
int n = input.nextInt();
// 加载数字进行统计
for (int i = 0;i < n;i++) {
int num = input.nextInt();
cha(num);
}
// 余数0与余数0配对,余数1与余数2配对
int ans = arr[0]/2 + Math.min(arr[1], arr[2]);
System.out.println(ans);
input.close();
}
public static void cha(int num) {
// 统计各个位数的和
int cout = 0;
while (num != 0) {
cout+= num%10;
num/= 10;
}
// 检查数字的余数
arr[cout%3]++;
}
}
购买数字
题目
小码哥是数学王国的一位居民,最近他迷上了回文数,决定去数字商店购买一个回文数。小码哥是个比较贪心的孩子,他想要购买自己能买到的最大的回文数,已知每个数的价格是这个数本身(即 1 是 1 块钱,2 是 2 块钱…)。
小码哥家由于是个数学世家,小码哥的零花钱也比别人多一点点,目前小码哥手上有 n 块钱,小码哥想问你他能买到的最大的回文数是多少?
格式
输入格式:一个正整数𝑛n,表示小码哥的零花钱,其中可能有前导零。
输出格式:输出一个正整数,表示小码哥能买到的最大的回文数。
取值范围:1 ≤ n ≤ 10200 ,数字的长度也小于等于 200 。
样例
输入:648
输出:646
问题分析
为了解决这个问题,我们需要分析如何构造最大的回文数,同时确保这个回文数的价格不超过小码哥手中的钱数。回文数是一个正读和反读都一样的数,例如 121、1331 等。
首先,因为输入可能有前导 0 ,因此我们应该先处理一下,创建双指针来指定数字的有效边界。通过循环比较数字是否为 0 ,将前导 0 排除。
接下来,我们可以开始构造回文数。由于我们想要得到最大的回文数,我们应该尽可能地保留高位的数字。因此,为了保证回文特性,我们应该尽可能得去改低位的数字。
当比较低位和高位数字是否为相等时,会出现两种情况。当低位数字大于高位数字时,例如 123456 ,则说明我们的钱是充裕的,我们只要将低位数字设置成跟高位数字一样的数字就行;当低位数字小于高位数字时,因为高位数字能不减小就不减小,所以我们低位要尝试去向前一位借钱。
当前一位也没钱时,就要继续向更前一位去借。由于借钱这个过程存在一种嵌套性,因此我们可以将它抽取成一个独立的方法,让其耦合度不要过高,条例也更清晰一点。借完钱后就将第二种情况变为第一种情况,这样就修改该位数就行,例如 423002,经过第一轮调整后就变成 422994 ,后三位都成功借到钱了。
仔细去想刚才的借钱流程,会发现有个极端情况可能出现问题,比如说 1000 ,经过第一轮借钱后会变成 0990 ,因此为了搞定这种特殊情况,我们可以在遍历完后,舍弃掉第一位的 0 ,再将末尾的 0 改回 9 ,这样就 OK 啦。
代码
import java.util.Scanner;
import java.util.*;
class Main {
static char[] cs;
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s = input.nextLine(); // 读入字符串
cs = s.toCharArray(); // 转换为字符串数组
int i = 0; // 左边界
int j = cs.length-1; // 右边界
while (cs[i] == '0') i++; // 不要前导 0
int cur = i; // 记录数字开始的索引,方便后面还原为字符串
while (i < j) {
// 如果构建不了汇文就要借数
if (cs[j] < cs[i]) {
borrow(j);
}
// 借完后更新为回文
cs[j] = cs[i];
// 比较下一位
i++;
j--;
}
// 如果是类似 1000 这样的数字,它会因为借数导致第一位变成 0
if (cs[cur] == '0') {
cur++; // 那就舍弃这一位 0
cs[cs.length-1] = '9'; // 将最后一位改回 9
}
// 截取转换为字符串
String ans = new String(cs, cur, cs.length-cur);
System.out.println(ans);
input.close();
}
// 如果不够就向前一位解数
public static void borrow(int j) {
// 如果前一位数为 0 ,那就继续往前走
if (cs[j-1] > '0') {
cs[j-1]--;
}else {
borrow(j-1);
}
// 返回的时候,之前为 0 的数也会更新为 9
cs[j] = '9';
}
}
最后
如果有所收获请点赞支持一下作者哦,您的点赞是我持续创作的动力!!