poj 3373 Changing Digits (DFS + 记忆化剪枝+鸽巢原理思想)

时间:2023-01-30 10:06:34

http://poj.org/problem?id=3373

Changing Digits
Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 2719   Accepted: 863

Description

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:

  1. m contains no leading zeros and has the same length as n (We consider zero itself a one-digit integer without leading zeros.)
  2. m is divisible by k
  3. among all numbers satisfying properties 1 and 2, m would be the one with least number of digits different from n
  4. 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≤104kn) 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

Source

 
[思路]:
看了别人的解题报告才写出来。一开始不知道怎么搜,没思路。
我们知道:假设k的位数是D,那么改变n的最后D+1位,能得到k+1的顺序数,由鸽巣原理,这k+1个数中至少有1个数能被k整除。所以,至多替换n的D+1位,肯定能找到结果。(1) 先搜比n小的数 
(2)再搜比n的大的数 ,这样就能保证只要搜出结果来了,就一定是最小值。
(3) 改变替换的个数 。直接这样搜,会TLE。
进行剪枝,增加一数组f[110][10010];
f[i][j] = c 表示  i 位置,当前余数为 j 时,剩余替换次数为 c ,index 在区间[0,c-1]时都不成立。
 
【code】:
 #include<iostream>
#include<stdio.h>
#include<string.h> using namespace std; #define N 110
#define NN 10010 char str[N];
int mod[N][N],ans[N],num[N],f[N][NN];
int k,len; int dfs(int pos,int m,int cnt)
{
int i,j;
if(m==) return ; //当余数为0时,表示已经找到,返回1
if(pos<||cnt<=f[pos][m]||cnt==) return ; //从前面最高位开始,从小到大遍历,保证得到的ans最小
for(i=pos;i>=;i--)
{
for(j=;j<num[i];j++)
{
if(i==len-&&j==) continue;
ans[i] = j;
int res = (m-(mod[i][num[i]]-mod[i][j])+k)%k; //注意+k防止出现负数
if(dfs(i-,res,cnt-)) return ; //进入下一层搜索
}
ans[i] = num[i]; //还原ans
} //从后面最低位开始,从小到大遍历,保证得到的ans最小
for(i=;i<=pos;i++)
{
for(j=num[i]+;j<;j++)
{
if(i==len-&&j==) continue;
ans[i] = j;
int res = (m+(mod[i][j]-mod[i][num[i]]))%k;
if(dfs(i-,res,cnt-)) return ;
}
ans[i] = num[i]; //还原
}
f[pos][m] = cnt;
// cout<<pos<<" "<<m<<" "<<cnt<<endl;
return ;
} int main()
{
while(~scanf("%s",str))
{
int i,j;
scanf("%d",&k);
memset(f,,sizeof(int)*(k+));
len = strlen(str);
for(i=;i<;i++) mod[][i]=i%k;
for(i=;i<len;i++)
{
for(j=;j<;j++)
{
mod[i][j] = (mod[i-][j]*)%k; //mod[i][j]: j*(10^i) 对 K 的取余 值
}
}
int m=;
for(i=;i<len;i++)
{
ans[i]=num[i]=str[len--i]-'';
m = (m + mod[i][ans[i]])%k; //获得str除以k的余数m
}
for(i=;i<=len;i++) if(dfs(len-,m,i)) break;
for(i=len-;i>=;i--) printf("%d",ans[i]);
putchar();
}
return ;
}