Given two positive integers n and k, you are asked to generate a new integer, say m, by changing some (maybe none) digits of n, such that the following properties holds:
m contains no leading zeros and has the same length as n (We consider zero itself a one-digit integer without leading zeros.)
m is divisible by k
among all numbers satisfying properties 1 and 2, m would be the one with least number of digits different from n
among all numbers satisfying properties 1, 2 and 3, m would be the smallest one
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.
Sample Input
2
2
619103
3219
Sample Output
2
119103
题意:
0算一个不含前导0的数
输入整数n,k,这两个数不含前导0
通过改变n每位上的数字但不改变n的长度从而产生一个数m,使得m满足以下4个条件
1.m与n位数相同
2.m%k==0
3.满足1.2的条件后,得到m尽量少的改动每位上的数字
4.满足1.2.3的条件后,求得的m尽量最小
注意:如果改动1位的m1和改动2位的m2,m1>m2,结果输出的是m1.y因为必须先要满足3,再考虑满足4
分析:
转载出处:http://exp-blog.com
思路:(搜索)
改变位数依次增加(优)&&从小到大搜索(次),一旦搜到,即是索要答案
注意
当m<n时,n先从高位->低位进行改变
当m>n时,n先从低位->高位进行改变
大佬的解析:
res=(m-(mod[i][num[i]]-mod[i][j])+k)%k;;
以数字 5234,k=27 为例,在本算法中数组m实际存放为数字的倒序4325,首位为第0位的“4”,同样 mod[][] 存放的是倒序数据。
数据存放: num[]=4325, k=27
已知 m=5234%27=23,现在要把ans的第3位的“5”修改为更小的“1”,已有 num[3]=5,设 j=1,即令num[3]=j,显然改变后实际的变化值为5000-1000=4000
我们现在要求的是m改变后的 1234%27 的值,而 1234%25=(5234%27 – 4000%27)%27。由于 5234%27=m=23 已知,那么只需求 4000%27。
而 4000%27=(5000%27-1000%27)%27,显然 5000%27 和 1000%27 已经保存在 mod[][] 中。 5000%27=mod[3][num[3] ] ,1000%27=mod[3][ j ],说到这里应该都了解了。
关于剪枝:
定义二维数组 int** f
令 f[pos][m]=cnt;
当搜索m的位置区间为 [0,pos],且当前数字串m对k取模值为 m时,
若剩下允许的修改数字的个数只允许修改cnt个,
则无论如何修改区间 [0,pos] 内的数字也无法使得 m==0 ,
那么对于同样的pos和 m, 小于cnt的个数则更加不可能了
#include<stdio.h>
#include<string.h>
#define N 110
#define NN 10010
char str[N];
int mod[N][10];//mod[][]能组合出任意len(n)位内的整数对k求模后的值
int ans[N];//倒存n,它会发生改变
int num[N];//倒存n,不会变,为了还原以前被改变的
int f[N][NN];//还不太懂
int k,len;
int dfs(int pos,int m,int cnt)//原5466换成6645
{//pos表示下标,m表示余数,cnt表示要改变的位数
int i,j;
if(m==0)
return 1; //当余数为0时,表示已经找到,返回1
if(pos<0||cnt<=f[pos][m]||cnt==0)
return 0;
//从前面最高位开始,从小到大遍历,保证得到的ans最小
for(i=pos;i>=0;i--)
{
for(j=0;j<num[i];j++)//必须m<n
{
if(i==len-1&&j==0)//(首位不可为0)
continue;
ans[i]=j;
int res=(m-(mod[i][num[i]]-mod[i][j])+k)%k; //注意+k防止出现负数
if(dfs(i-1,res,cnt-1))
return 1; //进入下一层搜索
}
ans[i]=num[i]; //还原ans
}
//从后面最低位开始,从小到大遍历,保证得到的ans最小
for(i=0;i<=pos;i++)
{
for(j=num[i]+1;j<10;j++)
{
ans[i]=j;
int res=(m+(mod[i][j]-mod[i][num[i]]))%k;
if(dfs(i-1,res,cnt-1))//[i-1不太懂]
return 1;
}
ans[i]=num[i]; //还原
}
f[pos][m]=cnt;
return 0;
}
int main()
{
while(~scanf("%s",str))
{
int i,j;
scanf("%d",&k);
memset(f,0,sizeof(int)*(k+1));
len=strlen(str);
/*对一个超级大的数取模,需要对这个数进行拆分
利用(a+b)%k=(a%k+b%k)%k和(a*b)%k=(a%k*b%k)%k
例如 111%5=(1+10+100)%5=(1%5+10%5+100%5)%5=(1%5+(1%5*10%5)%5;*/
//
for(i=0;i<10;i++)
mod[0][i]=i%k;//个位先取模
for(i=1;i<len;i++)//十,百,千...取模
for(j=0;j<10;j++)
mod[i][j]=(mod[i-1][j]*10)%k; //mod[i][j]: j*(10^i) 对 K 的取余值
//
int m=0;
for(i=0;i<len;i++)//个,百,千...所有余数相加再取模[(a+b)%k=(a%k+b%k)%k,延伸到n个数相加再取模]
{
ans[i]=num[i]=str[len-1-i]-'0';//ans,num倒存str
m=(m+mod[i][ans[i]])%k; //获得str除以k的余数m
}
for(i=1;i<=len;i++)
if(dfs(len-1,m,i))
break;
for(i=len-1;i>=0;i--)
printf("%d",ans[i]);
printf("\n");
}
return 0;
}