poj3373--Changing Digits(DFS+剪枝///记忆化)

题目链接:点击打开链接

题目大意:给出一个n和一个k 求m

要求1、m要和n相同的位数

要求2、m要整除k

要求3、如果1和2满足,那么m要和n有尽量少的不同位

要求4、如果1、2、3满足,要使m尽量的小

简单的一个深搜,但是直接被要求吓蒙,,,,,

要求1和2直接可以在搜索时判断,要求3可以在深搜时给出可以改变的位数(有0到len(n)),而要求4需要控制在搜索是要从小的开始搜,即从100000到999999,因为在深搜之前就控制了可以改变的次数,所以在搜索时不用担心要求3,只要使要求1要求2满足就可以,那么搜到的第一个就是最小的。

注意剪枝:

1、在每一次变化后都要直接计算出余数,当余数为0时,返回1,而不是一定要搜到最后一位。

mod[i][j] = (j*10^i)%k

2、flag[i][j]当搜到第i位余数为j时,没有找到结果的(修改位数),当以后遇到修改位数<=flag[i][j]时直接返回0。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
#define INF 0x3f3f3f3f
char str[110] ;
int k , len , a[110] ;
int mod[110][10]  ;
int flag[110][11000] ;
void init() {
    int i , j ;
    for(j = 0 ; j < 10 ; j++)
        mod[0][j] = j%k ;
    for(i = 1 ; i < 110 ; i++)
        for(j = 0 ; j < 10 ; j++)
            mod[i][j] = mod[i-1][j]*10%k ;
}
int dfs(int num,int pos,int s) {
    if( s == 0 ) return 1 ;
    if( num == 0 || pos == -1 ) return 0 ;
    if( num <= flag[pos][s] ) return 0 ;
    int i , temp ;

    for(i = 0 ; i <= 9 ; i++) {
        if( pos == len-1 && i == 0 ) continue ;
        if( i < a[pos] ) {
            temp = a[pos] - i ;
            a[pos] = i ;
            if( dfs(num-1,pos-1,(s-mod[pos][temp]+k)%k) ) return 1 ;
            a[pos] += temp ;
        }
        else if( i == a[pos] ) {
            if( dfs(num,pos-1,s) ) return 1 ;
        }
        else {
            temp = i-a[pos] ;
            a[pos] = i ;
            if( dfs(num-1,pos-1,(s+mod[pos][temp])%k) ) return 1 ;
            a[pos] -= temp ;
        }
    }
    flag[pos][s] = max(flag[pos][s],num);
    return 0 ;
}
int main() {
    int i , j , s , temp ;
    //freopen("1.txt","r",stdin) ;
    //freopen("2.txt","w",stdout) ;
    while( scanf("%s %d", str, &k) != EOF ) {
        memset(flag,-1,sizeof(flag)) ;
        len = strlen(str) ;
        for(i = len-1 ; i >= 0 ; i--) {
            a[len-1-i] = str[i] - '0' ;
        }
        init() ;
        for(i = s = temp = 0; i < len ; i++) {
            s = (mod[i][a[i]]+temp)%k ;
            temp = s ;
        }
        for(i = 0 ; i <= len ; i++) {
            if( dfs(i,len-1,s) ) break ;
        }
        for(i = len-1 ; i >= 0 ; i--)
            printf("%d", a[i]) ;
        printf("\n") ;
    }
    return 0 ;
}

/*
535064
9084
535956


19169 15724
15724

3902 153
3978
*/


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值