码迷,mamicode.com
首页 > 其他好文 > 详细

习题10-21 二项式系数 UVa1649

时间:2015-05-02 09:41:23      阅读:125      评论:0      收藏:0      [点我收藏+]

标签:枚举   二分法   

1.题目描述:点击打开链接

2.解题思路:本题利用枚举+二分解决。问题的关键是选对枚举对象,因为要找C(n,k)=m,如果枚举n的话,一旦m非常大,枚举n就会十分困难。因此枚举对象应为k。根据组合数的性质易知,C(n,n/2)时是最大值,C(n,1)是最小值。由于固定的是k,因此n=2*k时是最小的范围,n=m是最大的范围,这样,即可通过二分法来寻找n。

本题有一个技巧,即在计算组合数时候,不必完整的计算完毕,只要发现中间已经超过了m,说明这个n肯定不是解,返回m+1即表示无解。

3.代码:

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

#define pll pair<long long,long long>
#define M(a,b) make_pair(a,b)

using namespace std;
priority_queue<pll, vector<pll >, greater<pll > >q;
long long m;
long long c(int k, long long n)
{
	int i;
	long long f = 1;
	for (i = 1; i <= k; i++)
	{
		if ((f / i)>(m / (n - i + 1)))//如果发现算到第i步时结果已经超过m了,说明n肯定不是解,返回一个m+1
			return m + 1;
		f *= (n - i + 1);
		f /= i;
	}
	return f;
}
void chk()
{
	long long l, r, mid, t;
	int k;
	for (k = 1; c(k, 2 * k) <= m; k++)//最小的范围是C(k,2*k)
	{
		l = k * 2;
		r = m;
		while (l <= r)
		{
			mid = (l + r) >> 1;
			t = c(k, mid);
			if (t == m)
			{
				q.push(M(mid, k));
				if (mid == k * 2) break;
				q.push(M(mid, mid - k));
				break;
			}
			else if (t<m)
				l = mid + 1;
			else
				r = mid - 1;
		}
	}
}
int main()
{
	//freopen("t.txt", "r", stdin);
	int n, i;
	scanf("%d", &n);
	for (i = 1; i <= n; i++)
	{
		cin >> m;
		chk();
		if (q.size())
		{
			printf("%d\n", q.size());
			int len = q.size();
			for (int i = 0; i < len; i++)
			{
				printf("(%lld,%lld)%c", q.top().first, q.top().second, i == len - 1 ? '\n' : ' ');
				q.pop();
			}
		}
		else
			puts("0\n");
	}
	return 0;
}


习题10-21 二项式系数 UVa1649

标签:枚举   二分法   

原文地址:http://blog.csdn.net/u014800748/article/details/45424285

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!