K-th string (微软2014实习生及秋令营技术类职位在线测试题)

3 篇文章 0 订阅
2 篇文章 0 订阅

        微软的技术开发岗的一道测试题。

        题目要求如下:在由N个0和M个1组成的全排列中,选出第K个位置上的排列。

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

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 such 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”.

样例输入

    3
    2 2 2
    2 2 7
    4 7 47

样例输出

    0101
    Impossible
    01010111011

       这道题的关键点在于明白一个定理,即:假设有k种物体,每种物体的个数为ni个, 1 <= i <= k,则这k中物体的排列数为 n! / ( n1! * n2! * ... * nk!)种, 其中 n = Sum(n1 + n2 + .. + nk ). 在这道题中,分成2类物体0和1,它们的个数为N和M,所以它们的全排列数为(N+M)! / (N! * M!).

         下面以一个例子来说明如何使用上述定理来解这道题:假设我们要求由3个0和0个1组成的全排列中的第15个数。

           因为由3个0和0个1组成的全排列一共有 6! / (3! * 3!)  = 20种,所以一定存在解。

          (1)确定第一个 1:

           因为由3个1和2个0组成的全排列有 10 < 15, 在这种情况下,最大的数为 0 111 00

                   由3个1和3个0组成的全排列有 20 > 15, 在这种情况下,最大的数为 1111 000

          所以第15个数字一定在0 111 00 ~ 1111 000 之间, ==> 我们就可以确定 第15个数的第一个1的位置,即它满足 1** ***。

          下面我们需要确定那些 ** *** 具体代表什么。

         (2)确定** ***

         我们知道这5个*中,有2个为1,3个为0.

        我们已经知道1** *** 在排列中11 -- 20位,所以我们只需要找出 ** *** 中第5个位置上的排列n1 n2 n3 n3 n5, 那么排在第15位的数字就是1 n1 n2 n3 n3 n5。

        所以第二步就可以转换为求 由2个为1,3个为0组成的全排列中第5个数。下面就可以递归来求解了。

  

      本人写的代码如下:

#include <iostream>
#include <string>
#include <queue>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

long long T;
long long N, M, K;
long long KindNum[40][40]; // KindNum[i][j] = (i+j)! / (i! * j!)
int Array[30]; // Array[i] 表示排列中第i位的数字0/1

/*
    求出0-33的所有情况的排列数
*/
long long kind()
{
      memset(KindNum, 0, sizeof(KindNum));

      for(int i = 0; i <= 33; i++)
      {
          for(int j = 0; j <= 33; j++)
          {
              if(i == 0 || j == 0)
                KindNum[i][j] = 1;
              else
              {
                  KindNum[i][j] = KindNum[i][j-1] * (i + j) / j;
              }
              //cout <<  KindNum[i][j] << " ";
          }

          //cout << endl;
      }
}

/*
求由m个1,n个0组成的全排列中第k个位置的数
*/
void findNum(long long m, long long n, long long k)
{
    for(int i = 0; i <= 33; i++)
    {
        if(KindNum[m][i] == k) // 第K个数恰好是由m个1和i个0组成的排列中的最大数
        {
            int index = N + M -1;
            index -= i;

            while(m > 0)
            {
                Array[index] = 1;
                index--;
                m--;
            }

            return;
        }
        else if(KindNum[m][i] < k && KindNum[m][i+1] > k) // 确定第k个数在哪个区间
        {
            int index = (N + M -1) - (i + 1) - m + 1;

            Array[index] = 1; // 确定第index位为1

            findNum(m-1, i+1, k - KindNum[m][i]); // 递归查询

            return ;
        }
    }
}

int main()
{
    //freopen("data.txt", "r", stdin);

    kind();

    cin >> T;
    while(T--)
    {
        memset(Array, 0, sizeof(Array));

        cin >> N >> M >> K;

        if(KindNum[M][N] < K)
        {
            printf("Impossible\n");
        }
        else {
              findNum(M, N, K);

              for(int i = 0; i < N + M; i++)
                printf("%d", Array[i]);
              printf("\n");
        }
    }

    return 0;
}

       在C++ SLT中, next_permutation可以用来计算全排列的下一个排列。据说这道题可以使用它从第一个排列开始,一直调用k次 next_permutation,就可以求出第k的排列,并且不会超时。抓狂怎么可以这样,我们都是信奉“非暴力,不合作”的人!!!


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值