问题描述
小红拿到一个正整数,每次可以删除其中一个数位,但必须保证每次删除后,剩下的数字仍然是3的倍数且大于0。小红想知道最多可以进行多少次这样的删除操作?
解题思路
要解决这个问题,我们需要利用数字的数学特性:一个数字是3的倍数当且仅当它的各位数字之和是3的倍数。我们的目标是找到最长的删除序列,使得每次删除后剩余数字之和仍是3的倍数。
核心策略
-
统计数字信息:
- 计算数字总和
sum - 统计每位数字出现的次数
cnt - 统计模3余数为0、1、2的数字个数
mod
- 计算数字总和
-
根据总和模3分类处理:
-
情况1:总和模3等于0
- 优先保留一个非0的模0数字(如3,6,9),删除次数为
n-1 - 否则保留一个模1和一个模2的数字,删除次数为
n-2 - 否则保留三个模1或三个模2的数字,删除次数为
n-3
- 优先保留一个非0的模0数字(如3,6,9),删除次数为
-
情况2:总和模3等于1
- 保留一个模1和一个模2的数字,删除次数为
n-2 - 否则保留三个模1或三个模2的数字,删除次数为
n-3
- 保留一个模1和一个模2的数字,删除次数为
-
情况3:总和模3等于2
- 保留一个模2和一个模1的数字,删除次数为
n-2 - 否则保留三个模2或三个模1的数字,删除次数为
n-3
- 保留一个模2和一个模1的数字,删除次数为
-
完整代码实现
#include <stdio.h>
#include <string.h>
int main() {
int t;
scanf("%d", &t);
getchar(); // 消耗换行符
while (t--) {
char num[100001];
fgets(num, sizeof(num), stdin);
int n = strlen(num) - 1; // 去掉换行符
int cnt[10] = {0}; // 存储0-9每个数字的出现次数
int mod[3] = {0}; // 存储模0、1、2的数字个数
int sum = 0;
// 第一次遍历:计算总和和模3分布
for (int i = 0; i < n; i++) {
int digit = num[i] - '0';
sum += digit;
cnt[digit]++;
mod[digit % 3]++;
}
int r = sum % 3; // 总和模3的结果
int ans = 0;
if (r == 0) {
// 计算非0的模0数字个数(3,6,9)
int non_zero_mod0 = mod[0] - cnt[0];
if (non_zero_mod0 > 0) {
ans = n - 1; // 保留一个非0模0数字
} else if (mod[1] > 0 && mod[2] > 0) {
ans = n - 2; // 保留一个模1和一个模2
} else if (mod[1] >= 3) {
ans = n - 3; // 保留三个模1
} else if (mod[2] >= 3) {
ans = n - 3; // 保留三个模2
} else {
ans = 0; // 无法删除(理论上不会发生)
}
} else if (r == 1) {
if (mod[1] > 0 && mod[2] > 0) {
ans = n - 2; // 保留一个模1和一个模2
} else if (mod[1] >= 3) {
ans = n - 3; // 保留三个模1
} else if (mod[2] >= 3) {
ans = n - 3; // 保留三个模2
} else {
ans = 0; // 无法删除
}
} else if (r == 2) {
if (mod[2] > 0 && mod[1] > 0) {
ans = n - 2; // 保留一个模2和一个模1
} else if (mod[2] >= 3) {
ans = n - 3; // 保留三个模2
} else if (mod[1] >= 3) {
ans = n - 3; // 保留三个模1
} else {
ans = 0; // 无法删除
}
}
// 特殊情况:如果删除次数等于总位数,至少保留一位
if (ans == n) {
ans = n - 1;
}
printf("%d\n", ans);
}
return 0;
}
关键点解析
-
数学基础:利用数字和模3的性质判断3的倍数
-
贪心策略:优先保留最少数量的数字以满足条件
-
特殊情况处理:
- 避免保留单个0(不符合大于0的要求)
- 确保删除后剩余数字仍为正整数
- 处理无法删除的情况(返回0)
示例说明
-
输入 "123":
- 数字和6模3=0
- 存在非0模0数字(3)
- 删除次数=3-1=2(可删除1和2,保留3)
-
输入 "111":
- 数字和3模3=0
- 无有效非0模0数字
- 保留三个模1数字(1,1,1)
- 删除次数=3-3=0(无法删除任何数字)
-
输入 "112":
- 数字和4模3=1
- 存在模1和模2数字
- 删除次数=3-2=1(可删除一个1,保留12)
该算法高效解决了问题,时间复杂度为O(n),适用于大数字串处理。