【题意】
给出n和k,求一个数m,m是满足所有以下4个条件的数:(1)与n位数相同(2)能被k整除(3)与n相同位的数不相同最少(4)满足以上3个条件的最小值。其中1≤n≤10100 , 1≤k≤104,k≤n
【思路】
此题是看了别人的解题报告才写出来。一开始不知道怎么搜,没思路。我们知道:假设k的位数是D,那么改变n的最后D+1位,能得到k+1的顺序数,由鸽巣原理,这k+1个数中至少有1个数能被k整除。所以,至多替换n的D+1位,肯定能找到结果。(1)先搜比n小的数 (2)再搜比n的大的数 ,这样就能保证只要搜出结果来了,就一定是最小值。 (3)改变替换的个数 。直接这样搜,会TLE。进行剪枝,增加一数组remember[110][10010];remember[i][j]= c 表示 剩余替换次数为i,当前余数为j时,index 在区间[0,c-1]时都不成立。
【code】
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
char str[110];
int k, len;
int num[110], test[110];
int for_mod[110][10];
int remember[110][10010];
void init()
{
int i, j;
for(i = 0; i < 10; i++)
for_mod[0][i] = i % k;
for(i = 1; i < len; i++)
for(j = 0; j <10; j++)
for_mod[i][j]= (10 * for_mod[i-1][j]) % k;
memset(remember,0,sizeof(remember[0])*len);
}
bool DFS(int left, int index, int mod)
{
int i, j, tmp;
if(mod == 0)
{
for(i = len-1; i>= 0; i--)
printf("%d",test[i]);
printf("n");
return true;
}
if(remember[left][mod] > index ||left == 0)
return false;
for(i = index; i >= 0; i--)
{
for(j = 0; j <num[i]; j++)
{
if(i == len-1&& j == 0) //头不为0
continue;
tmp =for_mod[i][j] + mod - for_mod[i][num[i]];
tmp %=k;
if(tmp< 0)
tmp+= k;
test[i] =j;
if(DFS(left-1,i-1,tmp))
returntrue;
}
test[i] = num[i];
}
for(i = 0; i <= index; i++)
{
for(j = num[i]+1; j< 10; j++)
{
tmp =for_mod[i][j] + mod - for_mod[i][num[i]];
tmp %=k;
if(tmp< 0)
tmp+= k;
test[i] =j;
if(DFS(left-1,i-1,tmp))
returntrue;
}
test[i] = num[i];
}
remember[left][mod] = index + 1;
return false;
}
int main()
{
int i, mod;
while(scanf("%s",str) != -1)
{
scanf("%d",&k);
len = strlen(str);
mod = 0;
init();
for(i = 0; i <len; i++)
{
num[i] =test[i] = str[len-i-1] - '0';
mod +=for_mod[i][num[i]];
mod %=k;
}
for(i = 0;;i++)
{
if(DFS(i,len-1,mod))
break;
}
}
return 0;
}