任意給定一個正整數N,求一個最小的正整數M(M>1),使得N*M的十進制表示形式裏只含有1和0。
第一想法:從小到大枚舉M的取值,然後再計算N*M,最後判斷它們的乘積是否只含有1和0。該方法時間複雜度太高。
第二想法:因爲問題要求N*M的十進制表示形式裏只含有1和0,嘗試去搜索N*M,需要搜索的空間要小很多。
終極思路:(避免多於的除法驗證餘數)如1%3=1;10%3=1;101%3=2那麼我們可以知道110%3=2。即1和10加上相同的數,他們的餘數都相同,所以我們只需要考慮1,11,101,1001而不用考慮10,110,1010。對於modN同餘的數,只需要記錄最小的一個。形式化論述:假設已經遍歷了X的十進制表示有K位時的所有情況,而且也搜索了X=10^K的情況,設10K%N=a。現在要搜索X有K+1位的情況,即X=10K+Y,(0<Y<10K)。把Y按照其對N的餘數分類,我們搜索的空間將被分成N-1個子空間。對於每個子空間,其實只需要判斷其中最小的元素加上10K是否被N整除即可,而沒有必要判斷這個子空間裏所有元素加上10K是否能被N整除。
僞代碼描述:BigInt[i]表示模N等於i的十進制表示形式裏只含1和0的最小整數。由於BigInt[i]可能很大,又因爲它只有0和1,所以只需要記下1的位置即可。如1001記爲(0,3)=10^0+10^3。即BigInt的每個元素是一個變長數組,對於模N等於i的最小X,BigInt的每個元素將存儲最小X在十進制中表示1的位置。我們的目標就是求BigInt[0]。
public static String find01Int(int N){
intNoUpdate = 0;
LinkedList<Integer>[]kt = new LinkedList[N];
for(int i = 0;i<N;i++){
kt[i] = new LinkedList<Integer>();
}
kt[1].push(0);
//i=1從十位開始,顯然,個位1已經放入kt[1]了
for(int i = 1,j=10%N;;i++,j=(j*10)%N){
boolean flag = false;
if(kt[j].size()==0){
kt[j].clear();
kt[j].push(i);
}
for(int k = 1;k<N;k++){
//用於判斷含有餘數的項並且是低位(<i),並加上高位後產生的餘數是新的
//餘數,則把得出該餘數的最小值記錄下來
if((kt[k].size()>0)&&
(i>kt[k].peek())&&
(kt[(k+j)%N].size()==0)){
flag= true;
//這步賦值很重要,不能漏,因爲該最小值前面幾項在kt[k]中
for(int p = 0;p<kt[k].size();p++){
kt[(k+j)%N].add(kt[k].get(p));
}
kt[(k+j)%N].push(i);
}
}
if(flag==false)
NoUpdate++;
else
NoUpdate =0;
//如果經過一個循環都沒能對BigInt進行更新,就是無解,跳出,或者BigInt[0]已經找到也跳出
if(NoUpdate == N||kt[0].size()>0)
break;
}
if(kt[0].size() == 0){
return null;
}
else{
StringBuilder sb = new StringBuilder();
//輸出也很有技巧,例如4的話,kt[0]記錄的是2,此時,要輸出100,所以要注意//收尾操作
sb.append(1);
for(int i = kt[0].pop();;){
int temp = 0;
boolean t = true;
if(kt[0].size()!=0){
temp= kt[0].pop();
t= false;
}
for(int k = i-1;k>temp;k--){
sb.append(0);
}
if(!t)
sb.append(1);
else
sb.append(0);
i = temp;
if(kt[0].size()==0)break;
}
return sb.toString();
}
}