题意

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

思路

先说一个错误的思路,先计算出正整数的数位和,然后从头遍历字符串,如果删除这位后满足条件,就删除。这样无法得到最优解。

正解是根据数位和sum % 3 的值以及所有的数位里%3 的值的个数来讨论的,其实就是个思维题,但是边界条件太多了!

假设正整数的每一个数位%3等于1的个数为cnt1,数位%3等于2的个数为cnt2,数位%3等于0的个数为cnt0

题目要求删除后这个正整数是3的倍数,就说明删除后的sum%3一定为0

如果sum%3一开始就是0的话,那么只需要考虑cnt0的个数就可以。

但是如果数位和sum%3 =1的话,就需要先删除一位数位%3=1的使得sum%3=0,再考虑cnt0的个数。sum%3=2也同理。

以上就是这个题的基本思路,下面是各种边界条件处理:

  • 如果sum % 3 = 1 但是cnt1 = 0,也就是无法通过第一次删除使得sum%3=0,直接删除0即可。
  • 如果sum % 3 = 2 但是cnt2 = 0 也同理
  • 如果最后删除的个数ans 等于这个正整数的长度,说明把全部的数都删除了,ans需要减去1
  • 如果sum % 3 = 1 并且cnt1 = 1 ,也就是一开始只有一种删除的可能性,而且s[0] % 3 == 1,需要考虑删除了以后前导零的问题,样例2就是个很好的例子。这里要注意前导零造成影响的条件是删除这一位以后可以出现前导零,也就是第一位是1,后面跟着0才能有前导零影响

Go代码

package main

import (
	"fmt"
)

func main() {
	var T int
	fmt.Scan(&T)
	for i := 0; i < T; i++ {
		var s string
		fmt.Scan(&s)
		sum := 0
		cnt0, cnt1, cnt2 := 0, 0, 0
		for j := 0; j < len(s); j++ {
			now := int(s[j] - '0')
			sum += now
			switch now % 3 {
			case 0:
				cnt0++
			case 1:
				cnt1++
			case 2:
				cnt2++
			}
		}
	//	fmt.Println(cnt0, cnt1, cnt2, sum, sum%3)
        //一定不行的情况
        if sum % 3 == 1 && cnt1 == 0 {
            fmt.Println(0)
            continue
        }
        if sum % 3 == 2 && cnt2 == 0 {
            fmt.Println(0)
            continue
        }
		var ans int = cnt0
		//先把sum%3的数位删除一个 再删除%3=0的数位
		if sum%3 == 1 && cnt1 >= 1{
			ans ++
		}
		if sum%3 == 2 && cnt2 >= 1{
			ans ++
		}
		//不能全删除
		if ans == len(s) {
			ans--
		}
		//处理前导0
		if sum%3 == 1 && cnt1 == 1 && int(s[0]-'0') % 3 == 1 {
			for j := 1; j < len(s); j++ {
                if s[j] == '0' {
                    ans --
                }else{
                    break 
                }
			}
		}
		if sum%3 == 2 && cnt2 == 1 && int(s[0]-'0') % 3 == 2 {
			for j := 1; j < len(s); j++ {
                if s[j] == '0' {
                    ans --
                }else{
                    break 
                }
			}
		}
		fmt.Println(ans)
	}
}

/*
5
814788067
209592085
25
333
103252

3 0 0 2 2
*/