#P3322. 第3题-小红和红魔馆
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 42
            Accepted: 13
            Difficulty: 7
            
          
          
          
                       所属公司 : 
                              科大讯飞
                                
            
                        
              时间 :2025年7月26日
                              
                      
          
 
- 
                        算法标签>动态规划          
 
第3题-小红和红魔馆
题解思路与方法
整除条件拆分
因为 495=5×9×11,故需同时满足:
- 整除 5:末位为 0 或 5;
 - 整除 9:所有位数字和 ≡0(mod9);
 - 整除 11:交错位和(从最低位开始奇数位减、偶数位加)≡0(mod11)。
 
动态规划算法
- 
预处理符号
- 将输入的数字看作一个长度为 L 的字符数组,从左到右编号为 0…L-1。
 - 为了判断整除 11,需要对每一位乘以 +1 或 –1:最高位对应符号根据它从右边数过来的位置是偶数还是奇数来决定。简而言之,最右边第一位用 +1,第二位用 –1,第三位用 +1,以此类推。
 
 - 
定义状态
- 用三维数组 
dp[i][r9][r11]表示:当我们已经处理完前 i 位(也就是下标 0 到 i-1),并且到目前为止所有已选数字之和对 9 的余数是 r9、对 11 的“交错和”余数是 r11 时,最少要做多少次修改。 - 一开始(i=0),还没填任何数字,两个余数都算作 0,修改次数为 0;其余状态都初始化为“极大值”。
 
 - 用三维数组 
 - 
状态转移 对于第 i 位(0 ≤ i < L),我们尝试把它改成每一个可能的数字 d:
- 如果 i=0,就不能选 0,避免前导零;
 - 如果 i=L-1(最后一位),只能选 0 或 5,以保证整除 5;
 - 其它位置可以选 0 到 9。
 
每次尝试后:
- 新的“和 mod 9”是多少,就把旧的 r9 加上 d 再对 9 取余;
 - 新的“交错和 mod 11”是多少,就把旧的 r11 加上 当前位置的符号乘以 d,再对 11 取余;
 - 如果 d 和原来这一位不同,就要多算一次修改;
 - 用新的修改次数去更新 
dp[i+1][新r9][新r11]。 
 - 
答案与路径重构
- 最终看 
dp[L][0][0],那就是把整个数字改成 495 的倍数所需的最少修改次数。 - 同时我们在每一步记录“前驱”选择的数字和旧的 r9、r11,就能从尾到头把具体修改后得到的新数字串找出来。
 
 - 最终看 
 
复杂度分析
- 状态数 ≈L×9×11≤19×99≈1881;
 - 每状态枚举至多 10 个 d,总转移 ≲18810;
 - 路径重构 O(L)。 总体 O(L×9×11×10),L≤18 时远小于时间限制。
 
代码实现
Python 版本
import sys
def solve():
    s = sys.stdin.readline().strip()
    L = len(s)
    # 交错和符号
    sign = [1 if ((L-1-i)%2==0) else -1 for i in range(L)]
    INF = 10**9
    # dp[i][r9][r11]
    dp = [[[INF]*11 for _ in range(9)] for __ in range(L+1)]
    pre = [[[None]*11 for _ in range(9)] for __ in range(L+1)]
    dp[0][0][0] = 0
    for i in range(L):
        orig = int(s[i])
        for r9 in range(9):
            for r11 in range(11):
                if dp[i][r9][r11] == INF: continue
                for d in range(0, 10):
                    if i==0 and d==0: continue
                    if i==L-1 and d not in (0,5): continue
                    nr9 = (r9 + d) % 9
                    nr11 = (r11 + sign[i]*d) % 11
                    cost = dp[i][r9][r11] + (d != orig)
                    if cost < dp[i+1][nr9][nr11]:
                        dp[i+1][nr9][nr11] = cost
                        pre[i+1][nr9][nr11] = (r9, r11, d)
    # 输出结果
    ans = dp[L][0][0]
    print(ans)
    # 重构
    res = ['0']*L
    r9, r11 = 0, 0
    for i in range(L, 0, -1):
        pr = pre[i][r9][r11]
        res[i-1] = str(pr[2])
        r9, r11 = pr[0], pr[1]
    print("".join(res))
if __name__ == "__main__":
    solve()
Java 版本
import java.io.*;
import java.util.*;
public class Main {
    static final int INF = 1_000_000_000;
    public static void main(String[] args) throws Exception {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String s = in.readLine().trim();
        int L = s.length();
        int[] sign = new int[L];
        for (int i = 0; i < L; i++) {
            int pos = L-1-i;
            sign[i] = (pos%2==0 ? 1 : -1);
        }
        int[][][] dp = new int[L+1][9][11];
        Pre[][][] pre = new Pre[L+1][9][11];
        for (int i = 0; i <= L; i++)
            for (int r9 = 0; r9 < 9; r9++)
                for (int r11 = 0; r11 < 11; r11++)
                    dp[i][r9][r11] = INF;
        dp[0][0][0] = 0;
        for (int i = 0; i < L; i++) {
            int orig = s.charAt(i) - '0';
            for (int r9 = 0; r9 < 9; r9++) {
                for (int r11 = 0; r11 < 11; r11++) {
                    if (dp[i][r9][r11] == INF) continue;
                    for (int d = 0; d <= 9; d++) {
                        if (i==0 && d==0) continue;
                        if (i==L-1 && d!=0 && d!=5) continue;
                        int nr9 = (r9 + d) % 9;
                        int nr11 = (r11 + sign[i]*d % 11 + 11) % 11;
                        int cost = dp[i][r9][r11] + (d != orig ? 1 : 0);
                        if (cost < dp[i+1][nr9][nr11]) {
                            dp[i+1][nr9][nr11] = cost;
                            pre[i+1][nr9][nr11] = new Pre(r9, r11, d);
                        }
                    }
                }
            }
        }
        int ans = dp[L][0][0];
        System.out.println(ans);
        char[] res = new char[L];
        int cr9 = 0, cr11 = 0;
        for (int i = L; i > 0; i--) {
            Pre p = pre[i][cr9][cr11];
            res[i-1] = (char)('0' + p.d);
            cr9 = p.r9; cr11 = p.r11;
        }
        System.out.println(new String(res));
    }
    static class Pre {
        int r9, r11, d;
        Pre(int _r9, int _r11, int _d) { r9=_r9; r11=_r11; d=_d; }
    }
}
C++ 版本
#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9;
struct Pre { int r9, r11, d; };
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    string s;
    cin >> s;
    int L = s.size();
    vector<int> sign(L);
    for(int i=0; i<L; i++){
        int pos = L-1-i;
        sign[i] = (pos%2==0 ? +1 : -1);
    }
    static int dp[20][9][11];
    static Pre pre[20][9][11];
    for(int i=0; i<=L; i++)
        for(int a=0; a<9; a++)
            for(int b=0; b<11; b++)
                dp[i][a][b] = INF;
    dp[0][0][0] = 0;
    for(int i=0; i<L; i++){
        int orig = s[i]-'0';
        for(int r9=0; r9<9; r9++){
            for(int r11=0; r11<11; r11++){
                if(dp[i][r9][r11]==INF) continue;
                for(int d=0; d<=9; d++){
                    if(i==0 && d==0) continue;
                    if(i==L-1 && d!=0 && d!=5) continue;
                    int nr9  = (r9 + d) % 9;
                    int nr11 = (r11 + sign[i]*d % 11 + 11) % 11;
                    int cost = dp[i][r9][r11] + (d!=orig);
                    if(cost < dp[i+1][nr9][nr11]){
                        dp[i+1][nr9][nr11] = cost;
                        pre[i+1][nr9][nr11] = {r9, r11, d};
                    }
                }
            }
        }
    }
    int ans = dp[L][0][0];
    cout << ans << "\n";
    string t(L,'0');
    int cr9=0, cr11=0;
    for(int i=L; i>0; i--){
        auto &p = pre[i][cr9][cr11];
        t[i-1] = char('0'+p.d);
        cr9  = p.r9;
        cr11 = p.r11;
    }
    cout << t << "\n";
    return 0;
}
        题目内容
小红来到了红魔馆。众所周知,红魔馆的馆主是一只495岁的吸血鬼,所以她非常喜欢495这个数。现在,小红拿到了一个正整数n,她想选择n的某几位,然后依次将每一位数字改变成'0'到'9',中的一个,但是不允许出现前导零。
她想要使得该数字变成495的倍数,请你计算最少需要改变的位数(可以不改变),并给出改变后的数.
输入描述
在一行上输入一个正整数n(100≤n≤1018),表示原始数字
输出描述
一行输出一个整数,代表需要改变的最少位数。
第二行输出改变后的数。
样例1
输入
195
输出
1
495
样例2
输入
1145
输出
2
1485
样例3
输入
495
输出
0
495