题目大意:给定两个数据n和k让求出m,使得m可以被k整除,并且m是最优的:m和n的位数相同,不同的数字是最小的,如果不同的数字是相同的则取m的最小值!
大致思路:最优搜索加上记忆化搜索!
sign[l][mod]标记的是在l位置余数为mod找小于等于sign[l][mod]个替换的数字已经不可能了。
从最优开始搜索,所以数字改变的位数从0到n的长度,从最小的小于n的数开始搜索,如果不满足则从最小的大于n的数开始搜索,最先被k整除的m一定是最优解,然后就跳出搜索!
其中还用到了一些技巧,怎样在很短的时间内求出大数除以小数的余数等等,这些都是我以前没有见过的.
发现每次做完题然后等过了一周多就忘记了,看来以后的解题报告要等一段时间再写,可以回顾一下,不能刚做完题就写了!
#include<iostream> #include<string> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<queue> #include<stack> #include<vector> #include<climits> using namespace std; #define rep(i,n) for(i=0; i<(n); i++) #define repf(i,n,m) for(i=(n); i<=(m); i++)//正循环的 #define repd(i,n,m) for(i=(n); i>=(m); i--) //负循环的 #define fab(a) (a)>0?(a):0-(a) #define max(a,b) (a)>(b)?(a):(b) #define min(a,b) (a)<(b)?(a):(b) #define ll __int64 #define arc(a) (a)*(a) #define inf 102 //最大值的 #define exp 0.0000001 //浮点型的 #define N 105 //记录开的数组 int dp[N][10]; int sign[N][10000]; int test[N],num[N]; int len,k; void init() { int i,j; repf(i,0,9) dp[1][i]=i%k; repf(i,2,len) repf(j,0,9) dp[i][j]=(dp[i-1][j]*10)%k; memset(sign,0,sizeof(sign)); } bool dfs(int r, int l,int mod ) { int i,j; if(mod==0) { rep(i,len) printf("%d",test[i]); printf("\n"); return true; } if(r==0 ) return false; if(l>len-1) return false; if(sign[l][mod]>=r)//就是在l范围内已经不可以了 return false; repf(i,l,len-1) { repf(j,0,num[i]-1) { if(i==0 && j==0) continue; test[i]=j; int temp=mod-dp[len-i][num[i]]+dp[len-i][j]; temp%=k; if(temp<0) temp+=k; if(dfs(r-1,i+1,temp)) return true; } test[i]=num[i]; } repd(i,len-1,l) { repf(j,num[i]+1,9) { if(i==0 && j==0) continue; test[i]=j; int temp=mod-dp[len-i][num[i]]+dp[len-i][j]; temp%=k; if(temp<0) temp+=k; if(dfs(r-1,i+1,temp)) return true; } test[i]=num[i]; } sign[l][mod]=r; return false; } int main() { int i,j; char s[N]; while(~scanf("%s",s)) { len=strlen(s); scanf("%d",&k); init(); rep(i,len) test[i]=num[i]=s[i]-'0'; int mod=0; repd(i,len-1,0) mod=(mod+dp[len-i][num[i]])%k; // printf("%d\n",mod); for(i=0;;i++) if(dfs(i,0,mod)) break; } return 0; }