首页 > 其他 > 详细

POJ3252 Round Numbers 组合数学||数位DP

时间:2015-04-01 23:51:35      阅读:341      评论:0      收藏:0      [点我收藏+]

这题目做了两个小时,调试的头都晕了。。。


题型是数位DP中很常见的,给一个区间[l,r]求区间[l,r]中的 符合题目要求的数的个数,本题求的 是 区间中 的数  它的二进制中0的个数大于等于1的个数的    这些数的个数,不好意思表达能力有点差。。。例如[1,4]答案是2, 因为 2的二进制是10,0的个数大于等于1的个数,4的二进制是100,0的个数大于1的个数,所以答案是两个


好了第一反应肯定是 设定一个函数 f(r) - f[l - 1]就是答案,那么显然这个函数f(r)的意思就是 1 到 r之间符合要求的数的个数,很容易想到数位DP,但是真心不好推啊,由于推数位DP的过程中对每个数的二进制 都画了一下,后来发现了 组合数学其实也是可以做的,

首先每一个数化为二进制数  第一位肯定是1,那么剩下的位数中 让一定的位数 上的数字为0就可以达到目的,例如 1 {}{}{}  其中{}表示空位,剩下的三个空位中至少让两个为0即可,那么答案其实就是 C(3,2) + C(3,3),跟组合数联系上了,那么就好办了

首先 把一个数m处理成二进制数,假设长度为n,那么 所有 长度为[1,n-1]的数 中 合法的 个数很容易就求出来了,直接扫一遍 然后判断它的长度再求组合数即可

接下来的部分就是分析 长度等于n的 且  大小必须小于等于m的 数中  合法的个数,有点麻烦,就是扫他的二进制数,若扫到的当前位上的数字为1,那么就令这个位置上的数为0,这样当前二进制表示的数 肯定是小于m的,这样就对于还未扫到的位 上的数 进行组合数求解答案即可,具体见代码中的注释部分


郁闷的是,我做的时候脑子有点糊涂,做到一半曲解了题目的意思,求的是 [l,r]中 数的二进制中1的个数大于0的个数的  这些数的个数,还好答案直接由总数减一下就可以了。。。不然就惨了。。。



int s,e;

int bin[33];

const int MAXN = 33;
int C[33+1][33+1];

int cnt = 0;

void Initial() {
	int i,j;
	for(i=0; i<=33; ++i) {
		C[0][i] = 0;
		C[i][0] = 1;
	}
	for(i=1; i<=33; ++i) {
		for(j=1; j<=33; ++j)
		C[i][j] = (C[i-1][j] + C[i-1][j-1]);
	}
}

void init() {

}

bool input() {
	while(cin>>s>>e) {
		return false;
	}
	return true;
}

void Binary(int n) {
	memset(bin,0,sizeof(bin));
	cnt = 0;
	while(n) {
		bin[cnt++] = n&1;
		n >>= 1;
	}
}

int slove(int n) {
	if(n == 0)return 0;
	int ret = 0;
	Binary(n);
	for(int i=cnt - 1;i>0;i--) {//统计所有二进制数长度小于n的,可以自由组合数
		int tmp = (i - 1);
		if(tmp&1)tmp = (tmp>>1) + 1;
		else tmp >>= 1;
		for(int j=tmp;j<=i - 1;j++) {
			ret += C[i - 1][j];
			//cout<<C[i - 1][j]<<endl;
		}
	}
	int mark1 = 1,mark2 = 0;
	for(int i=cnt - 1;i>0;i--) {//统计长度与n相同的且小于等于n的
		if(bin[i - 1]) {
			int tmp;
			if(cnt&1)tmp = (cnt + 1)>>1;
			else tmp = (cnt>>1) + 1;//长度为cnt的至少需要多少个1
			tmp = tmp - mark1;//减去前面已经扫到的1的个数,当前至少还需几个1
			if(tmp < 0)tmp = 0;//有可能前面已经超过了需要的1的个数,这里需要注意,置0,WA出翔
			for(int j= tmp;j<=i - 1;j++)
				ret += C[i - 1][j];
			mark1++;//这个必须放最后,因为当前为0,不是必须为1,调试了半年
		}
		else mark2++;
	}
	ret += mark1>mark2?1:0;//写到最后发现我无意曲解了题意,
	ret = n - ret;//我求的是一个数的二进制中1大于0的个数,那么好办求反就可以了
	return ret;
}

void cal() {
	cout<<slove(e) - slove(s - 1)<<endl;
}

void output() {

}

int main() {
	Initial();
	while(true) {
		init();
		if(input())return 0;
		cal();
		output();
	}
	return 0;
}



POJ3252 Round Numbers 组合数学||数位DP

原文:http://blog.csdn.net/yitiaodacaidog/article/details/44815075

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!