pku 3373 Changing Digits 爆搜+强剪枝

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

题目意思:

给出2个整数N(n<10^100)和K(k<10000),求满足以下条件的整数M
1、M与N位数相同
2、M能被K整除
3、满足以上两点时,M和N不同位数最少

4、满足以上三点时,M值最小

思路:

题目意思很好理解,我们只要以n为基础分两个方向搜索即可,

1:首先搜索比n小的,这样保证在为数不同的前提下,M值最小;

2:搜索比n大的,

详细的解题报告:http://blog.csdn.net/lyy289065406/article/details/6698787

解题报告中说只要改变n的5位就一定能够找到解(由鸽巢定理得 k最多为4位),自己没能理解,希望有人能够给解释。

View Code
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;

const int maxn=109;

int rem[maxn][maxn*maxn];
int ans[maxn],num[maxn],mod[maxn][12];
char s[maxn];
int K,len,Mod;

void init()
{
    int i,j;
    memset(rem,0,sizeof(rem));
    //mod[i][j]寸10^i*j%K的值
    for (i = 0; i < 10; ++i) mod[0][i] = i%K;
    for (i  = 1; i < len; ++i)
    {
        for (j = 0; j < 10; ++j)
        mod[i][j] = (mod[i - 1][j]*10)%K;
    }
    //计算s模K的余数,将s倒置
    Mod = 0;
    for (i = 0; i < len; ++i)
    {
        ans[i] = num[i] = s[len - i - 1] -'0';
        Mod += mod[i][num[i]];
        Mod %= K;
    }
}
bool dfs(int left,int pos,int M)
{
    int i,j;
    if (M == 0)//余数为0得到解
    {
        for (i = len - 1; i >= 0; --i) printf("%d",ans[i]);
        printf("\n");
        return true;
    }
    if (left == 0 || rem[left][M] > pos) return false;//关键的剪枝
    //搜索比N小的从高位开始
    for (i = pos; i >= 0; --i)
    {
        for (j = 0; j < num[i]; ++j)
        {
            if (i == len - 1 && j == 0) continue;
            ans[i] = j;
            int tmp = M - (mod[i][num[i]] - mod[i][j]);
            tmp%=K;
            if (tmp < 0) tmp += K;
            if (dfs(left- 1,i - 1,tmp)) return true;
        }
         ans[i] = num[i];
    }
    //搜索比n大的从低位开始
    for (i = 0; i <= pos; ++i)
    {
        for (j = num[i] + 1; j < 10; ++j)
        {
            ans[i] = j;
            int tmp = M + mod[i][j] - mod[i][num[i]];
            tmp%=K;
            if (tmp < 0) tmp += K;
            if (dfs(left - 1,i - 1,tmp)) return true;
        }
        ans[i] = num[i];
    }
    rem[left][M] = pos + 1;
    return false;
}
int main()
{
    //freopen("d.txt","r",stdin);
    int i;
    while (~scanf("%s",s))
    {
        scanf("%d",&K);
        len = strlen(s);
        init();
        for (i = 0; i <= len; ++i)
        {
            if (dfs(i,len - 1,Mod)) break;
        }
    }
    return 0;
}

 

 

 

posted @ 2012-08-11 15:15  E_star  阅读(272)  评论(0编辑  收藏  举报