POJ_3373_Changing Digits_DP

3 篇文章 0 订阅
本文探讨了一种解决特定数字转换问题的方法,目标是在保持数字长度不变、不引入前缀零且能够被指定整数整除的前提下,通过最小化修改位数来实现。采用动态规划(DP)技术,特别关注改变位数最少的原则,通过逐位迭代和状态转移矩阵优化求解过程。此方法不仅减少了计算复杂度,还确保了结果的最优性。适用于计算机科学领域中数字处理与优化的相关问题。
摘要由CSDN通过智能技术生成
好想去游泳。

题意

一个不超过100位的数字n和一个不超10000的数字k,改变其中的一些数字(可以改变0个数字),得到一个新的数字m,满足下列条件:

  1. 长度和n相同,除0外无前缀0
  2. m可以被k整除
  3. m尽可能改变少的数字
  4. 3优先的情况下m尽可能小

IO

Input

There are multiple test cases for the input. Each test case consists of two lines, which contains n(1≤n≤10100) and k(1≤k≤104, k≤n) for each line. Both n and k will not contain leading zeros.

Output

Output one line for each test case containing the desired number m.

分析

做出来以后看了看题解,发现用搜索做的人还用了抽屉原理,然后推出来只需要改变5位就可以搜出来因此只搜最后五位。然而这道题条件的优先级是改变数量少优先于改变后数字小,因此并没有什么卵用。
我用的是DP+记录DP路径,这题的DP搜索顺序挺坑爹的。
dp[i][j]定义为到第i位,除k余数为j时需要改变的数量。
dp[i][j]=min(dp[i][j], dp[ii][jj]),遍历当前位的数字l来递推,ii,jj为另一个i, j编号
min操作使得递推肯定抱枕改变的位数最小。
由于需要使数值最小,高位数字需要更大权利,因此从后向前遍历i,每一层i选择当前数字l都从0到9遍历,余数最不重要放在最里层。i从后向前遍历的原因可以从终点那里看出来。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define MXL 110
#define MXK 10010
#define INF 0x3f3f3f3f
char a[MXL];
int k;
int dp[MXL][MXK];
int pre[MXL][MXK];
int rec[MXL][MXK];
char ans[MXL];
int anscur;
int main(){
    while(scanf("%s%d",a,&k)!=EOF){
        int len=strlen(a);
        anscur=0;
        memset(ans,0,sizeof(ans));
        memset(dp,0x3f,sizeof(dp));
        memset(pre,-1,sizeof(pre));
        dp[len][0]=0;
        int t=1;
        for(int i=len;i>0;--i){
            if(i!=len)  t*=10;
            t%=k;
            for(int l=0;l<10;++l)
                for(int j=0;j<k;++j)    if(dp[i][j]!=INF){
                    if(len>1&&i==1&&l==0)   continue;
                    int tem=1-(l==a[i-1]-'0');
                    if(dp[i][j]+tem<dp[i-1][(j+l*t)%k]){
                        dp[i-1][(j+l*t)%k]=dp[i][j]+tem;
                        pre[i-1][(j+l*t)%k]=j;
                        rec[i-1][(j+l*t)%k]=l;
                    }
                }
        }
        int nowl=0,nowj=0;
        while(nowl<len){
            ans[anscur++]=rec[nowl][nowj]+'0';
            nowj=pre[nowl][nowj];++nowl;
        }
        printf("%s\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值