poj3373——Changing Digits

题目大意:给出两个正整数n和k,改变n中的数字变为m,使得m能被k整除,要求改变的数字个数最少,在此基础上求得的m最小

输入:(可以有多个case)

            第i个case的n(1≤n≤10100) (1≤k≤104, k≤n)

            第i个case的k

输出:第i个case的m

分析:dfs+剪枝(以下分析参考了http://blog.csdn.net/lyy289065406/article/details/6698787/)

           本题有大量的取模运算,所以先预处理mod[i][j]((10^i)*j模k的值),后面dfs就可以根据递推式mod[i][j]=(mod[i-1][j]*10)%k来避免n次方的运算,节省时间,也就是说可以用mod[][]数组组合出任意与n长度相同的数模k的值。根据题意要求,要确定dfs顺序,首先允许改变的数字个数从1~len(n)枚举,这样能保证改变的数字个数最小,然后就是求得的m最小,搜索顺序如下:(数字n按逆序存储,也就是pos位其实是n的高位)

          (1)搜索时先搜索比n小的数字,此时为了搜索到的解m是最小的,应该从区间[0,pos]的第pos位开始向第0位搜索,因为在相同RestNum情况下,把高位数字变小所得到的m值更小。

          (2)当(1)无解时,搜索比n大的数字,此时为了搜索到的解m是最小的,应该从区间[0,pos]的第0位开始向第pos位搜索,因为在相同RestNum情况下,把低位数字变大所得到的m值更小。

           下面介绍一下改变数字后如何求改变后的m模k的值:

           搜索比n小的数字时,当把m的第i位数字m[i]改为j时,我们已经有((10^i)*m[i])%k的值存放在数组mod[i][m[i]]中,又有((10^i)*j)%k的值存放在数组mod[i][j]中,那么把m值改小前后的变化值为(mod[i][m[i]]- mod[i][j]),然后我们又知道m改变前的模k值为m_MODk。那么只需把m改变前的m_MODk减去变化值(mod[i][m[i]]- mod[i][j]),再对k取模,就是新的m_MODk值,我们令其等于res。而为了避免m_MODk < (mod[i][m[i]]- mod[i][j])而出现负数,我们应该增加一个“+k”修正处理,那么具体公式如下:

           res=(m_MODk-(mod[i][m[i]]-mod[i][j])+k)%k;

           搜索比n大的数字时,道理差不多,只不过变化值相减的方向要改变。而且由于是m_MODK+变化值,即不会出现负数,因此无需“+k”修正,具体公式如下:

           res=(m_MODk+(mod[i][j]-mod[i][m[i]]))%k;

           下面说一下剪枝:

           flag[][]数组代表当搜索范围为0~pos,数字串m模k为m_MODk时的Restnum

           也就是说当最多改变restnum位都不能成功时,改变个数小于restnum就更不可能成功了

代码:转载自https://www.cnblogs.com/fenshen371/p/3250109.html

1 #include<stdio.h>
 2 #include<string.h>
 3 #define maxn 103
 4 #define mk 10003
 5 int len, k, n[maxn], mod[maxn][10];
 6 int m[maxn], flag[maxn][mk];
 7 char num[maxn];
 8 void init_mod()//mod[i][j]表示(10^i)*j模k的值
 9 {
10     for (int i = 0; i <= 9; i++)
11         mod[0][i] = i % k;
12     for (int i = 1; i < len; i++)
13         for (int j = 0; j <= 9; j++)
14             mod[i][j] = (mod[i-1][j] * 10) % k;
15 }
16 int dfs(int pos,int restnum,int m_modk)//当前搜索区域为[0,pos]  剩余可以改变的个数restnum
17 {                                                                 //当前数字串m模k的值
18     if (!m_modk) return 1;
19     if (!restnum || pos < 0) return 0;
20     if (restnum <= flag[pos][m_modk]) return 0;//剪枝
21     for (int i = pos; i > -1; i--)//搜索比n[i]小的数,要尽可能小,则从高位开始
22         for (int j = 0; j < n[i]; j++)//循环搜索将m的i位改成j
23         {
24             if (i == len - 1 && !j) continue;//m的最高位不能为0
25             int res = (m_modk - (mod[i][n[i]] - mod[i][j]) + k) % k;
26             m[i] = j;
27             if (dfs(i - 1, restnum - 1, res))//缩减搜索区间
28                 return 1;
29             m[i] = n[i];
30         }
31     for (int i = 0; i <= pos; i++)//搜索比n[i]大的数,要尽可能小,则从低位开始
32         for (int j = n[i] + 1; j < 10; j++)
33         {
34             int res = (m_modk + (mod[i][j] - mod[i][n[i]]) + k) % k;
35             m[i] = j;
36             if (dfs(i - 1, restnum - 1, res))
37                 return 1;
38             m[i] = n[i];
39         }
40     flag[pos][m_modk] = restnum;//能运行到这里说明搜索失败,更新剪枝数值
41     return 0;
42 }
43 int main()
44 {
45     while (~scanf("%s%d", num, &k))
46     {
47         int n_modk = 0;
48         len = strlen(num);
49         init_mod();
50         for (int i = 0; i < len; i++)//将num反序存入整型数组
51         {
52             n[i] = num[len-1-i] - '0';
53             m[i] = n[i];
54             n_modk = (n_modk + mod[i][ n[i] ]) % k;//计算n % k
55         }
56         memset(flag, 0, sizeof(flag));
57         int ok = 0;
58         for (int i = 1; i <= len; i++)//从小到大枚举可以修改的位数
59             if (dfs(len - 1, i, n_modk))
60                 break;
61         for (int i = len - 1; i > -1; i--)
62             printf("%d", m[i]);
63         printf("\n");
64     }
65     return 0;
66 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值