USACO Stringsobits 解题报告

可以直接移步这里:大神的代码http://belbesy.wordpress.com/2012/08/13/usaco-3-2-2-stringsobits/

这道题让求第I个N位数,要求这些数的二进制表达中1的个数小于L。

刚开始用暴力解法,从1开始递增,算每个数的二进制表达中1的个数,并计数。不出所料地通不过后面的大测试点。即使用了快速计算二进制中1的个数的方法(见代码)。

在看了大神的代码后开始使用动态规划的方法。

cnt[n][k] = cnt[n - 1][k] + cnt[n - 1][k - 1]

这个表达式的左侧表示n位二进制数其中1的个数小于等于k的个数。右边的表达式把这样的数分为两类:第n位为0的(第一部分)和第n位为1的(第二部分,剩下的n-1位至多有k-1个1)。

最后计算要求的数的时候从左往右逐位判断:

比如第n位,看后面n-1位满足条件的数的个数(cnt[n-1][k])是否大于I,如果是,则第n位是0,继续在后面n-1位组成的数中找(n-1位中有k个1的第I个数);如果不是,则说明要找的数第n位是1,把n位是0的满足条件的数的个数(cnt[n-1][k])减去(因为这些数小,排在前面),就是这个数在n位是1的满足条件的数中的排位,相当于接下来在n-1位中k-1个1的第I-cnt[n-1][k]个数。

代码如下,前面两个函数为求一个数中的二进制表达中1的个数的方法,第一个快于第二个,但都超时,因而用不上。

/*
ID: thestor1
LANG: C++
TASK: kimbits
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cassert>
#include <string>
#include <algorithm>

using namespace std;

int bitcount(unsigned int n) {
	/* works for 32-bit numbers only    */
	/* fix last line for 64-bit numbers */

	register unsigned int tmp;
	//octal notation
	tmp = n - ((n >> 1) & 033333333333) - ((n >> 2) & 011111111111);
	return ((tmp + (tmp >> 3)) & 030707070707) % 63;
}

inline int cntones(int num)
{
	int cnt = 0;
	while(num > 0)
	{
		num &= num - 1;
		cnt++;
	}
	return cnt;
}

string tobinary(unsigned int num, unsigned int width)
{
	string str;
	int w = 0;
	while(num > 0)
	{
		str.push_back('0' + (num & 0x01));
		num >>= 1;
		w++;
	}
	string ret;
	for(int i = w; i < width; ++i)
	{
		ret.push_back('0');
	}
	for(int i = str.length() - 1; i >= 0; --i)
	{
		ret.push_back(str[i]);
	}
	return ret;
}

int main()
{
	FILE *fin  = fopen ("kimbits.in", "r");
	FILE *fout = fopen ("kimbits.out", "w");
	//freopen("log.txt", "w", stdout);

	unsigned int N, L, I;
	fscanf(fin, "%u%u%u", &N, &L, &I);

	unsigned int cnt[32][32] = {{0}};
	for(unsigned int n = 0; n <= N; ++n)
	{
		cnt[n][0] = 1;
		cnt[0][n] = 1;
	}
	for(unsigned int n = 1; n <= N; ++n)
	{
		for(unsigned int k = 1; k <= L; ++k)
		{
			cnt[n][k] = cnt[n - 1][k] + cnt[n - 1][k - 1];
		}
	}

	unsigned int n = N, k = L;
	while(n > 0)
	{
		fprintf(stdout, "cnt[%u - 1][%u] = %u, I = %u\n", n, k, cnt[n - 1][k], I);
		if(cnt[n - 1][k] >= I)
		{
			fprintf(stdout, "%u\n", I);
			fprintf(fout, "0");
			n--;
		}
		else
		{
			fprintf(fout, "1");
			I -= cnt[n - 1][k];
			n--;
			k--;
		}
	}
	fprintf(fout, "\n");
	
	return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值