高效解决 2.K-th string (微软2014在线笔试,第二题)

Time Limit: 10000ms
Case Time Limit: 1000ms

Memory Limit: 256MB



Description

Consider a string set that each of them consistsof {0, 1} only. All strings in the set have the same number of 0s and 1s. Writea 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 setwith 6 different strings, {0011, 0101, 0110, 1001, 1010, 1100}, and the 4thstring is 1001.


Input

The first line of the input file contains a singleinteger t (1 ≤ t ≤ 10000), the number of test cases, followed by the input datafor each test case.
Each test case is 3 integers separated by blankspace: 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 asoutput.


Output

For each case, print exactly one line. If thestring exists, please print it, otherwise print “Impossible”. 


Sample In

3
2 2 2
2 2 7
4 7 47

Sample Out

0101
Impossible

01010111011

本问题属于排序问题,寻找第K位数

对于已排好序的数组则时间复杂度为O(1)

未排好序的数组,时间复杂度为O(N)(N为数组大小)

本问题如果要得到排序,首先时间复杂度就会很高,为O(C(N,M+N))

思路很简单,就是排列组合的知识。

 

但是这样做复杂度太高。

采用计数的方式,计算全排列个数,则可以将时间复杂度降到O(M+N),这里假设计算排列组合的时间复杂度为O(1)(事实上,确实可以做到,只是需要使用公式)

 

循环:

 

如果减少1个0后的排序个数仍大于K,则可以在首位放一个0,组合数变为C(N-1,M+N-1)

 

小于K,则首位放置一个1,但是这时应该更新K,因为放置1之后,余下部分的排列数中已经减少了一部分,剩下的排列组合数只需要补足余下的个数就可以了,组合数变为C(N,M+N)

 

若等于K,则退出循环,剩余的0和1只需按最大排列即可。即1在前面,0在后面。

 

具体减少多少呢?


减少量 =  当前组合数 – 取走1之后的组合数

K = K – 减少量

 

这样循环下去,只需要最多M+N次,能得到该Kth 字符串;

 

具体边界条件比较简单,参看代码即可。

(代码水平比较渣,凑合看吧,lz正在努力学习中。没有参加微软的笔试,所以只是实现了思路,具体OJ的话,代码应该也比较好改)


 附:第一次发博客,转载注明啊~


#include <iostream>
using namespace std;
int Count(int up, int down){
	if (up > down / 2)
		up = down - up;
	long long fenzi = 1;
	long long fenmu = 1;
	for (int i = 1; i <= up; ++i)
		fenmu *= i;
	for (int i = down - up + 1; i <= down; ++i)
		fenzi *= i;
	return fenzi / fenmu;
}

bool getK(int zero, int one, int K, char* res){
	int up = zero;
	int down = zero + one;
	int C = Count(up, down);
	if (C < K) 
		return false;
	int index = 0;
	while (C!=K && up < down && up > 0)
	{
		if (Count(up-1,down-1) >= K) {
			up--;
			down--;
			res[index] = '0';
		}
		else{
			down--;
			K =  K - (C - Count(up,down));
			res[index] = '1';
		}
		C = Count(up, down);
		index++;
	}
	int tmp = index - 1;
	for (; index <= tmp + down - up; ++index)
		res[index] = '1';
	tmp = index - 1;
	for (; index <= tmp + up; ++index)
		res[index] = '0';
	res[index + 1] = '\0';
	return true;
}

int main()
{
	char *res = new char[16];
	if (getK(2, 2, 6, res)) 
		cout << res << endl;
	else
		cout << "Impossible" << endl;
	delete [] res;
	system("pause");
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值