题目描述 考虑排好序的N(N<=31)位二进制数。 你会发现,这很有趣。因为他们是排列好的,而且包含所有可能的长度为N且含有1的个数小于等于L(L<=N)的数。 你的任务是输出第I(1<=I<=长度为N的二进制数的个数)大的,长度为N,且含有1的个数小于等于L的那个二进制数。 输入要求 共一行,用空格分开的三个整数N,L,I。 样例输入 5 3 19 输出要求 共一行,输出满足条件的第I大的二进制数。 样例输出 10011 这题之前我做了很久都没有做出来,后来去看了某人的题解后才做出来。 我们先设一个数组F,F[i,j]表示长度为i且当前序列1的个数 小于 j的所有方案数。 每一位都有0或1两种可能,对应F[i-1,j]和F[i-1,j-1]两种情况。 状态转移方程: F[i,j]=F[i-1,j-1]+F[i-1,j] (i=1~n j=1~n) 初始化: F[i,0]=1 (i=0~n) F[0,i]=1 (i=1~l) 求出了F数组后,我们就可以通过F数组求出最后的结果。 用样例来说明: ?0011,现在要把?处补上,有0和1两种可能。 那么到底是0还是1呢? 我们要求第i位的数字,就要看它的后一位的结果是多少。 例如要求第5位的数字,我们就要看第4位的结果。 因为F[4,3]=15,而19>15,所以这一位是1。 F[4,3]=15,意味着已0和1开头的长度为5且1的个数小于3的情况有15种,而要求的结果19大于(一定是大于,因为第15种开头还是0)15,所以是1。 之后把19减去15,接着往下计算。 以此类推。 鉴于此题较难,所以直接上代码: var f:array[0..31,0..31] of longint; n,l,i,j:longint; p:int64; begin readln(n,l,p); for i:=0 to n do f[i,0]:=1; for i:=1 to l do f[0,i]:=1; for i:=1 to n do for j:=1 to n do f[i,j]:=f[i-1,j-1]+f[i-1,j]; for i:=n-1 downto 0 do if f[i,l]<p then begin p:=p-f[i,l]; dec(l); write(1); end else write(0); writeln; end.