题意:有序集合S中的元素都为N位的二进制数,这个集合S中的二进制数最多只有L个1,输出这个集合中第I个元素的二进制表示方法
解题思路:
- DP问题,设f[i][j] = k,代表最多有j位为1的i位二进制数集合的大小为k。那么这个集合中的二进制数可以分为最高位为0和为1这两类,所以f[i][j] = f[i-1][j] + f[i-1][j-1]。
- 边界情况f[i][0] = 1,f[0][j] = 1。
- 两重循环i, j可以计算出矩阵f[i][j]。
- 下面计算集合中第I个元素的二进制表示,类似与二分查找。
- 初始化最高位为0的集合大小为left = f[N-1][L],最高位为1的集合大小为right = f[N-1][L-1]。如果L == 0,right = 0。
- 如果I <= left,那么第I个数最高位为0,如果I > left,那么第I个数的最高位为1。
- 如果此时最高位为0,那么N = N -1,如果此时最高位为1,那么N = N -1,L = L - 1, I = I - left。
- 递归循环5~7,直到N-1 < 0。在此过程中可以得到各个位置的数字。
代码:
/*
ID: zc.rene1
LANG: C
PROG: kimbits
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
FILE *fin, *fout;
int N, L;
unsigned long I;
int **f;
int i, j, left, right;
fin = fopen("kimbits.in", "r");
fout = fopen("kimbits.out", "w");
fscanf(fin, "%d %d %lu", &N, &L, &I);
f = (int **)malloc((N+1)*sizeof(int *));
for (i=0; i<(N+1); i++)
{
f[i] = (int *)malloc((L+1)*sizeof(int));
memset(f[i], 0, (L+1)*sizeof(int));
}
for (i=0; i<(N+1); i++)
{
f[i][0] = 1;
}
for (j=0; j<(L+1); j++)
{
f[0][j] = 1;
}
for (i=1; i<=N; i++)
{
for (j=1; j<=L; j++)
{
f[i][j] = f[i-1][j] + f[i-1][j-1];
}
}
while (1)
{
if ((N - 1) < 0)
{
break;
}
left = f[N-1][L];
if (L == 0)
{
right = 0;
}
else
{
right = f[N-1][L-1];
}
if (I <= left)
{
N = N - 1;
fprintf(fout, "0");
}
else
{
N = N - 1;
L = L - 1;
I = I - left;
fprintf(fout, "1");
}
}
fprintf(fout, "\n");
return 0;
}