USACO section 3.2 Stringsobits(组合数学)

Stringsobits
Kim Schrijvers

Consider an ordered set S of strings of N (1 <= N <= 31) bits. Bits, of course, are either 0 or 1.

This set of strings is interesting because it is ordered and contains all possible strings of length N that have L (1 <= L <= N) or fewer bits that are `1'.

Your task is to read a number I (1 <= I <= sizeof(S)) from the input and print the Ith element of the ordered set for N bits with no more than L bits that are `1'.

PROGRAM NAME: kimbits

INPUT FORMAT

A single line with three space separated integers: N, L, and I.

SAMPLE INPUT (file kimbits.in)

5 3 19

OUTPUT FORMAT

A single line containing the integer that represents the Ith element from the order set, as described.

SAMPLE OUTPUT (file kimbits.out)

10011

思路:用组合做,如原题的5 3 19 那10011怎么来的呢, 也就说有5个位,你可以选3个是1,2个是1,1个是1不选1总情况数为26种而19<26,那看下一位,即是

           4位里选3位(即是假设输入是 4 3 19)那就是有4位可选3个1,2个1,1个1,0个1总情况数为19>15说明4位装不下,不到19个那么第5位一定得是1,

            即是1xxxx(x代表未知)假设m为输入的I,m原先等于19,当第5位确定为1后m=m-15=4;那原题转化为求4 2 4的输出只要求出4 2 4的输出,也就是求1xxxx

           后面的四个x;同样的3位可选2个1,1个1,0个1 有7>4;也就是说3位可以装的下那第四位就为0,原题转化为求3, 2 4的输出即是求10XXX后面的三个未知数,重复操作

           直到m化为0,得到结果。

注意点:31位,貌似int存会出错,C(n,m)=C(n,m-1)+C(n-1,m-1);dp(n,m)=C(n,m)+dp(n-1,m);

 

/*
ID:nealgav1
LANG:C++
PROG:kimbits
*/
#include<fstream>
using namespace std;
ifstream cin("kimbits.in");
ofstream cout("kimbits.out");
long long cc[55][55];///组合算C(n,m);m里取n
long long dp[55][55];///算dp(n,m)=C(n,m)+C(n-1,m)+...+C(0,m);
char s[55];///存2进制答案
long long C(int aa,int bb)
{
  cc[0][0]=1;dp[0][0]=1;
  for(int i=1;i<=bb;i++)
  { cc[0][i]=1;dp[0][i]=1;
   for(int j=1;j<=i;j++)
   {cc[j][i]=cc[j][i-1]+cc[j-1][i-1];
    dp[j][i]=cc[j][i]+dp[j-1][i];
   }
 }
}
int main()
{
  int b,a;
  long long m;
  cin>>b>>a>>m;
  s[b]='\0';
  C(a+1,b+1);
  int j;
  for(int i=b;i>=0;i--)
  { if(i>a)j=a;
    else j=i;
    if(m>0)
    {    ///当i为不够放时i+1位为1,同时减去i位能存量
      if(dp[j][i]<m){m-=dp[j][i];s[b-i-1]='1';a--;}
      else s[b-i-1]='0';
    }
    else s[b-i-1]='0';
  }
  //if(m)s[b-1]='1';
  cout<<s<<endl;
}


 

 

 

USER: Neal Gavin Gavin [nealgav1]
TASK: kimbits
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.000 secs, 3404 KB]
   Test 2: TEST OK [0.000 secs, 3404 KB]
   Test 3: TEST OK [0.000 secs, 3404 KB]
   Test 4: TEST OK [0.000 secs, 3404 KB]
   Test 5: TEST OK [0.000 secs, 3404 KB]
   Test 6: TEST OK [0.000 secs, 3404 KB]
   Test 7: TEST OK [0.000 secs, 3404 KB]
   Test 8: TEST OK [0.000 secs, 3404 KB]
   Test 9: TEST OK [0.000 secs, 3404 KB]
   Test 10: TEST OK [0.000 secs, 3404 KB]
   Test 11: TEST OK [0.000 secs, 3404 KB]
   Test 12: TEST OK [0.000 secs, 3404 KB]
   Test 13: TEST OK [0.000 secs, 3404 KB]

All tests OK.

Your program ('kimbits') produced all correct answers! This is your submission #3 for this problem. Congratulations!

Here are the test data inputs:

------- test 1 ----
4 2 1
------- test 2 ----
1 1 2
------- test 3 ----
8 4 30
------- test 4 ----
10 2 56
------- test 5 ----
7 7 64
------- test 6 ----
18 3 300
------- test 7 ----
21 10 1048576
------- test 8 ----
24 20 12936478
------- test 9 ----
31 24 10000000
------- test 10 ----
31 31 2147483648
------- test 11 ----
31 26 12345678
------- test 12 ----
31 26 123456789
------- test 13 ----
31 26 1234567890
Keep up the good work!
Stringsobits
Russ Cox

Suppose we knew how to calculate the size of the set of binary numbers for a given nbits and nones. That is, suppose we have a function sizeofset(n, m) that returns the number of n-bit binary numbers that have at most m ones in them.

Then we can solve the problem as follows. We're looking for the ith element in the set of size n with m bits. This set has two parts: the numbers the start with zero, and the numbers that start with one. There are sizeofset(n-1, m) numbers that start with zero and have at most m one bits, and there are sizeofset(n-1, m-1) numbers that start with one and have at most m one bits.

So if the index is less than sizeofset(n-1, m), the number in question occurs in the part of the set that is numbers that start with zero. Otherwise, it starts with a one.

This lends itself to a nice recursive solution, implemented by "printbits".

The only difficult part left is calculating "sizeofset". We can do this by dynamic programming using the property described above:

	sizeofset(n, m) = sizeofset(n-1, m) + sizeofset(n-1, m-1)

and sizeofset(0, m) = 1 for all m. We use double's throughout for bits, but that's overkill given the rewritten problem that requires only 31 bits intead of 32.

/*
PROG: kimbits
ID: rsc001
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

FILE *fout;

/* calculate binomial coefficient (n choose k) */
double sizeofset[33][33];

void
initsizeofset(void)
{
	int i, j;

	for(j=0; j<=32; j++)
		sizeofset[0][j] = 1;

	for(i=1; i<=32; i++)
	for(j=0; j<=32; j++)
		if(j == 0)
			sizeofset[i][j] = 1;
		else
			sizeofset[i][j] = sizeofset[i-1][j-1] + sizeofset[i-1][j];
}

void
printbits(int nbits, int nones, double index)
{
	double s;

	if(nbits == 0)
		return;

	s = sizeofset[nbits-1][nones];
	if(s <= index) {
		fprintf(fout, "1");
		printbits(nbits-1, nones-1, index-s);
	} else {
		fprintf(fout, "0");
		printbits(nbits-1, nones, index);
	}
}

void
main(void)
{
	FILE *fin;
	int nbits, nones;
	double index;

	fin = fopen("kimbits.in", "r");
	fout = fopen("kimbits.out", "w");
	assert(fin != NULL && fout != NULL);

	initsizeofset();
	fscanf(fin, "%d %d %lf", &nbits, &nones, &index);
	printbits(nbits, nones, index-1);
	fprintf(fout, "\n");

	exit(0);
}

 

posted @ 2012-10-24 16:58  剑不飞  阅读(239)  评论(0编辑  收藏  举报