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; }