好想去游泳。
题意
一个不超过100位的数字n和一个不超10000的数字k,改变其中的一些数字(可以改变0个数字),得到一个新的数字m,满足下列条件:
- 长度和n相同,除0外无前缀0
- m可以被k整除
- m尽可能改变少的数字
- 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;
}