送上今年微軟的一道筆試題

這裏送上一道微軟的筆試題,具體題目以下:html

Time Limit: 10000ms算法

Case Time Limit: 1000mside

Memory Limit: 256MBspa

 

Description3d

Consider a string set that each of them consists of {0, 1} only. All strings in the set have the same number of 0s and 1s.htm

Write a program to find and output the K-th string according to the dictionary order. If s​uch a string doesn’t exist,blog

or the input is not valid, please output 「Impossible」. For example, if we have two ‘0’s and two ‘1’s,遞歸

we will have a set with 6 different strings, {0011, 0101, 0110, 1001, 1010, 1100}, and the 4th string is 1001.ip

 

Inputget

The first line of the input file contains a single integer t (1 ≤ t ≤ 10000),

the number of test cases, followed by the input data for each test case.

Each test case is 3 integers separated by blank space: N, M(2 <= N + M <= 33 and N , M >= 0),

K(1 <= K <= 1000000000). N stands for the number of ‘0’s, M stands for the number of ‘1’s,

and K stands for the K-th of string in the set that needs to be printed as output.

 

Output

For each case, print exactly one line. If the string exists, please print it, otherwise print 「Impossible」.

 

Sample In

3

2 2 2

2 2 7

4 7 47

Sample Out

0101

Impossible

01010111011

其實說了這麼多,意思很簡單,就是輸入N個0,M個1.而後求出由M,N個1,0組成的第K大的數。若是不存在則輸出impossible.

初來乍到的,你們可能會認爲這是一個全排列的問題,可是全排列在問題在於不能很好的知道每一個數到底排在第幾位,而且時間上確定是不能經過的,遞歸的效率你們應該都知道。

咱們可能會想到另一種解決方案,直接列舉,從最小的000...1111開始,一直列舉到1111..000而後記錄下當前是不是含N個0,M個1。這種方式是最容易理解的了,可是若是數字比較大,好比17個1,17個0,咱們且不說這麼大的整數不能保存,就這個時間上也不合算啊,雖然他是線性複雜度,可是這個線性數也太大了點。。。

OK,我接下來又想到了是否能夠經過樹的遍歷,想了想被我否了。

終於想到了一種方式,就是經過不斷的交換得到。咱們想到,若是從一個第1大的數變成第2大的數,必然要使這個數增大,那麼怎麼個增大法?才能使得這兩個數是最接近的,也就是說咱們只增長了1,而不是中間還存在不少數呢?

那就是從左到右掃描數據,直到遇到10就交換,而且在該1以前的1要和最低位的0交換。好的思路已經徹底暴露了,我這裏就列舉一個例子。

好比5個1,5個0的狀況。

咱們保存的格式是1111100000(實際數爲0000011111),它是最小的數(輸出的時候倒着輸出,這種結構主要爲了理解方便)。最大的數是0000011111.(記住,是倒着哈!)

當咱們要增長的時候在J=5的時候,出現了0,且j=4時,它爲1,這樣就交換他們,j=4以前的1和低位的0交換,這裏沒有0就不須要交換了。獲得1111010000(實際數爲0000101111).

當咱們出現0011101100(實際數爲0011011100)要使數字增長1應該怎麼作呢?顯然,仍是J=5時出現了0且j-1=4時爲1交換他們,而且j=4以前的1和最低位的0開始不斷交換,最後咱們會獲得結果:1100011100.(實際數爲00111000011).

Ok,說到這裏你們應該就徹底懂了,且看算法源代碼:[集思廣益,大家有沒有更好的解決方案?]

  1. //M:0的個數,N,1的個數。K要輸出第幾個數。 
  2. bool test(int N,int M,int K){ 
  3.     if(calculateTotalNum(M+N,M)<K)//若實際上的數少於k,返回false,則輸出impossible 
  4.         return false; 
  5.     int L=M+N,count=1; 
  6.     bitset<MaxLength> bit; 
  7.     map<int,bitset<MaxLength>> mapStr; 
  8.     for(int i=0;i<M;i++)//初始話最小數,即0都在最左邊好比0011 
  9.         bit[i]=1; 
  10.     if(K==1){ 
  11.         print(bit,M+N);//輸出 
  12.         return true; 
  13.     } 
  14.     int j=0;//表示從低位一直搜索到高位,有沒有遇到01,有的話就不斷交換。 
  15.     while(!isLast(bit,M,N)){//沒有搜索到最後一個數字 
  16.         if(j>=M+N-1) 
  17.             j=0;//已經搜索到最高位了,這個時候就須要從0位搜索 
  18.         if(1==bit[j]&&0==bit[j+1]){ 
  19.             int right=j-1,left=0; 
  20.             //從J的前一個搜素,而且該以前的1所有移動到最左邊 
  21.             while(right>=0&&bit[right]==1&&left<right){ 
  22.                 bool temp=bit[right]; 
  23.                 bit[right]=bit[left]; 
  24.                 bit[left]=temp; 
  25.                 right--,left++; 
  26.             } 
  27.             bit[j]=0;//交換0,1 
  28.             bit[j+1]=1; 
  29.             count++;//統計是第幾個大的數了 
  30.             if(count==K){ 
  31.                 print(bit,M+N); 
  32.                 return true; 
  33.             } 
  34.             j=0;//從新回過頭來搜素 
  35.         } 
  36.         else 
  37.             j++; 
  38.     } 
  39.     return true; 
  40.  
  41. int calculateTotalNum(int N,int M){//組合問題,計算一共多少個數。C(M,N)=A(N,N)/(A(M,M)*A(N-M,N-M)) 
  42.     int result=1; 
  43.     for(int i=1;i<=N;i++) 
  44.         result*=i; 
  45.     for(int i=1;i<=M;i++) 
  46.         result/=i; 
  47.     for(int i=1;i<=N-M;i++) 
  48.         result/=i; 
  49.     return result; 
  50. bool isLast(bitset<MaxLength> bit,int M,int N){ 
  51.     int i=0; 
  52.     while(i<N&&0==bit[i]) 
  53.         i++; 
  54.     if(i==N) 
  55.         return true; 
  56.     else 
  57.         return false; 
  58. void print(bitset<MaxLength> bit,int N){// 
  59.     for(int i=N-1;i>=0;i--) 
  60.         cout<<bit[i]; 
  61.     cout<<""; 

 

附上效果截圖:

[庸男勿擾] 同窗提供了一種其餘的解決方式,主要是經過遞歸的判斷當前0,1組合生成的個數與K進行比較。

(保證在不減一個0時,生成的組合總數是大於K的,不然return。)

若當前0的個數減一後,生成的總數要大於K,則輸出0,同時0的個數減一,K,1的個數不變。

若當前0的個數減一後,生成的總數小於K,則輸出1,同時1的個數減一,0的個數不變,K=K-當前總數。

遞歸調用。最後能獲得結果。

代碼 [庸男勿擾] 已經貼出,在回答留言處!

[有一個問題是,我和庸男勿擾在計算總次數的時候都會有溢出的問題,即便用Long long也會溢出的,你們在計算階乘或者組合問題對溢出解決方案有什麼好的建議能夠給出嗎?] 

原文連接:http://www.cnblogs.com/xiaoyi115/p/3696507.html

相關文章
相關標籤/搜索