K-th string微软2014在线笔试题

Description

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. 
Write a program to find and output the K-th string according to the dictionary order. If s​uch a string doesn’t exist, 
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.


Input

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.

主要是通过递归的判断当前0,1组合生成的个数与K进行比较。

  (保证在不减一个0时,生成的组合总数是大于K的,否则return。)

   若当前0的个数减一后,生成的总数要大于K,则输出0,同时0的个数减一,K,1的个数不变。

   若当前0的个数减一后,生成的总数小于K,则输出1,同时1的个数减一,0的个数不变,K=K-当前总数。

   递归调用。最后能得到结果。

import java.util.Scanner;

public class Main {

    public static void print(Object[] res) {
        for (Object obj : res) {
            System.out.print(obj + " ");
        }
        System.out.println();
    }
    public static int getC(int m, int n){
        int maxNum = 1;
        int mm = Math.max(m, n);
        int nn = Math.min(m, n);
        for(int i = m + n; i > mm; i --){
            maxNum*= i;
        }
        for(int i = 1; i <= nn; i ++){
            maxNum/= i;
        }
        return maxNum;
    }
    public void solve(int num1, int num0, StringBuffer sb, int k){
        if(num0 < 0 || num1 < 0)return;
        if(num1 == 0 && num0 == 0) return ;
        if( k == 0){
            while(num1 > 0){
                sb.append(1);
                num1 --;
            }

            while(num0 > 0) {
                sb.append(0);
                num0--;
            }
            return ;
        }
        if(num0 > 0 && getC(num1, num0 - 1) > k){
            sb.append(0);
            solve(num1, num0 - 1, sb, k);
        }
        else if(num0 > 0 && getC(num1, num0 - 1) < k){
            sb.append(1);
            solve(num1 - 1, num0, sb, k - getC(num1, num0 - 1));
        }
        else if(num0 > 0 && getC(num1, num0 - 1) == k){
            sb.append(0);
            solve(num1, num0 - 1, sb, 0);
        }
    }
    public static void main(String[] args) {
        Main obj = new Main();
        Scanner input = new Scanner(System.in);
        int cas = input.nextInt();
        int m , n, k;
        StringBuffer sb = new StringBuffer("");
        while ((cas -- ) > 0){
            n = input.nextInt();
            m = input.nextInt();
            k = input.nextInt();
            sb.delete(0, sb.length());
            if(getC(m, n) < k) System.out.println("Impossible");
             else {
                obj.solve(m, n, sb, k);
                System.out.println(sb.toString());
            }
        }

    }

}

网上分析的另一种思路:

N个0和M个1的组合问题。 下面是需代码。
解释:
dp[n][m]:n个0和m个1一共可能的组合数。
dp[n][m]递推公式: dp[n][m] = dp[n - 1][m] + dp[n][m - 1]。 表示:如果最高位是1的组合数为dp[n][m - 1], 如果最高位为0, 组合数为dp[n - 1][m].

如何生成第K个数字:
如果dp[n][m] < K, 返回"Impossible". (最大组合数都小于序号)。

如果最高位是0的组合数大于等于K(dp[n][m - 1] >= K), 那么最高为一定为0。
如果最高位是0的组合数小于K, 那么最高位一定为1。 此时,因为最高位为0的组合有dp[n][m-1]个,问题变成:在N个0和(M-1)个1中需要为(K - dp[n][m-1])的数字是多少。

这是两个简单的动态规划的组合。其实dp数组中是杨辉三角,古人的智慧是无穷的!

另外,因为题目给定了M,N,K的取值范围,所以代码里面没有做合法性检查。
算法复杂度 O(M * N)

#include <iostream>
 
#include <vector>
#include <map>
#include <string>
using  namespace  std;
 
string calc( int  N,  int  M,  int  K) {
   vector<vector< long  long > > dp(N + 1, vector< long  long >(M + 1));
 
   for  ( int  n = 0; n <= N; n++) {
     for  ( int  m = 0; m <= M; m++) {
       if  (n == 0 || m == 0) {
         dp[n][m] = 1;
       else  {
         dp[n][m] = dp[n - 1][m] + dp[n][m - 1];
       }
     }
   }
 
   if  (dp[N][M] < K) {
     return  "Impossible" ;
   }
   string res =  "" ;
   while  (N >= 1 && M >= 0 && K > 0) {
     if  (dp[N - 1][M] >= K) {
       res +=  "0" ;
       N--;
     else  {
       res +=  "1" ;
       K -= dp[N - 1][M];
       M--;
     }
   }
   while  (M > 0) {
     res +=  "1" ;
     M--;
   }
   return  res;
}
 
int  main() {
   // N: 0
   // M: 1
   int  k;
   cin >> k;
   int  N;
   int  M;
   int  K;
   for  ( int  i = 0; i < k; i++) {
     cin >> N >> M >> K;
     cout << calc(N, M, K) <<  "\n" ;
   }
   return  0;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值