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

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≤104, kn) 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】:
 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 
 5 using namespace std;
 6 
 7 #define N 110
 8 #define NN 10010
 9 
10 char str[N];
11 int mod[N][N],ans[N],num[N],f[N][NN];
12 int k,len;
13 
14 int dfs(int pos,int m,int cnt)
15 {
16     int i,j;
17     if(m==0)    return 1;  //当余数为0时,表示已经找到,返回1
18     if(pos<0||cnt<=f[pos][m]||cnt==0)   return 0;
19 
20     //从前面最高位开始,从小到大遍历,保证得到的ans最小
21     for(i=pos;i>=0;i--)
22     {
23         for(j=0;j<num[i];j++)
24         {
25             if(i==len-1&&j==0)  continue;
26             ans[i] = j;
27             int res = (m-(mod[i][num[i]]-mod[i][j])+k)%k; //注意+k防止出现负数
28             if(dfs(i-1,res,cnt-1))    return 1;  //进入下一层搜索
29         }
30         ans[i] = num[i];  //还原ans
31     }
32 
33     //从后面最低位开始,从小到大遍历,保证得到的ans最小
34     for(i=0;i<=pos;i++)
35     {
36         for(j=num[i]+1;j<10;j++)
37         {
38             if(i==len-1&&j==0)  continue;
39             ans[i] = j;
40             int res = (m+(mod[i][j]-mod[i][num[i]]))%k;
41             if(dfs(i-1,res,cnt-1))    return 1;
42         }
43         ans[i] = num[i];  //还原
44     }
45     f[pos][m] = cnt;
46   //  cout<<pos<<" "<<m<<" "<<cnt<<endl;
47     return 0;
48 }
49 
50 int main()
51 {
52     while(~scanf("%s",str))
53     {
54         int i,j;
55         scanf("%d",&k);
56         memset(f,0,sizeof(int)*(k+1));
57         len = strlen(str);
58         for(i=0;i<10;i++)   mod[0][i]=i%k;
59         for(i=1;i<len;i++)
60         {
61             for(j=0;j<10;j++)
62             {
63                 mod[i][j] = (mod[i-1][j]*10)%k;     //mod[i][j]:  j*(10^i) 对 K 的取余 值
64             }
65         }
66         int m=0;
67         for(i=0;i<len;i++)
68         {
69             ans[i]=num[i]=str[len-1-i]-'0';
70             m = (m + mod[i][ans[i]])%k;   //获得str除以k的余数m
71         }
72         for(i=1;i<=len;i++)  if(dfs(len-1,m,i))    break;
73         for(i=len-1;i>=0;i--)   printf("%d",ans[i]);
74         putchar(10);
75     }
76     return 0;
77 }
原文地址:https://www.cnblogs.com/crazyapple/p/3225514.html