小红删除数字问题:贪心策略与数学原理

44 阅读3分钟

问题描述

小红拿到一个正整数,每次可以删除其中一个数位,但必须保证每次删除后,剩下的数字仍然是3的倍数且大于0。小红想知道最多可以进行多少次这样的删除操作?

解题思路

要解决这个问题,我们需要利用数字的数学特性:​​一个数字是3的倍数当且仅当它的各位数字之和是3的倍数​​。我们的目标是找到最长的删除序列,使得每次删除后剩余数字之和仍是3的倍数。

核心策略

  1. ​统计数字信息​​:

    • 计算数字总和 sum
    • 统计每位数字出现的次数 cnt
    • 统计模3余数为0、1、2的数字个数 mod
  2. ​根据总和模3分类处理​​:

    • ​情况1:总和模3等于0​

      • 优先保留一个非0的模0数字(如3,6,9),删除次数为 n-1
      • 否则保留一个模1和一个模2的数字,删除次数为 n-2
      • 否则保留三个模1或三个模2的数字,删除次数为 n-3
    • ​情况2:总和模3等于1​

      • 保留一个模1和一个模2的数字,删除次数为 n-2
      • 否则保留三个模1或三个模2的数字,删除次数为 n-3
    • ​情况3:总和模3等于2​

      • 保留一个模2和一个模1的数字,删除次数为 n-2
      • 否则保留三个模2或三个模1的数字,删除次数为 n-3

完整代码实现

#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;
}

关键点解析

  1. ​数学基础​​:利用数字和模3的性质判断3的倍数

  2. ​贪心策略​​:优先保留最少数量的数字以满足条件

  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),适用于大数字串处理。