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”.
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) 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) 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]
如果最高位是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
#include
#include
#include
using
namespace
std;
string
calc(
int
N,
int
M,
int
K) {
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]
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() {
int
k;
cin >> k;
int
N;
int
M;
int
K;
for
(
int
i = 0; i
cin >> N >> M >> K;
cout <
"\n"
;
}
return
0;
};