UVa-12558埃及分数

迭代加深搜索

题目大意对于一个分数a/b,用若干单位分数(1/a,a是自然数)的和来表示 ,加数越少越好,如果加数个数相同,最小的分数越大越好,同时给出了k个数,不能作为分母出现。最小的分数大,即最大的分数尽量小,其分母尽量大。

在例题求埃及分数的基础上,加上禁用限制就可以了。

#include <cstdio>
#include <set>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;

set<LL> ban;

const int N = 10005;
LL maxd;
LL ans[N], v[N];

//找到1/x<=a/b的最小的x 
int get_first( int a, int b ) {
	int res = b/a;
	return res*a>=b ? res : res+1;
}

inline LL gcd( LL a, LL b ) {
	return b==0 ? a : gcd(b,a%b);
}

bool better( int d ) { //更新当前的解
	for( int i=d; i>=0; --i ) {  //由于分母是由小到大存储的,因此逆序枚举
		if( v[i]!=ans[i] ) {
			return ans[i]==-1||v[i]<ans[i];
		}
	}
	return false;
}

bool dfs( int d, int from, LL aa, LL bb ) {
	if( d==maxd ) {
		if( bb%aa ) return false;  //如果不能整除,搜索失败
		if( ban.count(bb/aa) ) return false;
		v[d] = bb/aa;
		if( better(d) ) memcpy( ans,v,sizeof(LL)*(d+1) );
		return true;
	}
	//更新from这一步容易忽略,假设第d-1个分数的分母是a,第d个分数的分母不一定要从a+1开始,还要考虑1/(a+1)是否小于等于aa/bb
	from = max( from,get_first(aa,bb) );
	bool ok = false;
	for( int i=from;  ; ++i ) {
		if( (maxd+1-d)*bb<=aa*i ) break;   //此处才是正确的停止枚举的条件
		if(ban.count(i)) continue;
		v[d] = i;
		//计算aa/bb - 1/i的分子,分母
		LL b2 = bb*i;
		LL a2 = aa*i - bb;
		int g = gcd( a2,b2 ); //计算最大公约数,进行约分
		if( dfs(d+1,i+1,a2/g,b2/g) ) ok = true;  //注意,不要写成return true,因为找到一组解并不能当做停止枚举的条件
	}
	return ok;
}

int main()
{
	int a,b,k;
	int kase = 1;
	int T;
	scanf("%d",&T);
	while( T-- ) {
		scanf("%d%d%d", &a,&b,&k);
		ban.clear();
		for( int i=0; i<k; ++i ) {
			LL u;
			scanf("%ld",&u);
			ban.insert( u );
		}
		int ok = 0;
		for( maxd = 1; ; ++maxd ) {   //迭代加深搜索的主体框架,maxd要设置为全局变量
			memset( ans,-1,sizeof(ans) );
			if( dfs(0,get_first(a,b),a,b) ) {
				ok = 1; break;
			}
		}
		printf("Case %d: %d/%d=", kase++, a, b);
		for( int i = 0; i<=maxd; ++i ){
			if (i)  printf("+");
			printf("1/%ld", ans[i]);
		}
		printf("\n");
	}
	return 0;
}



猜你喜欢

转载自blog.csdn.net/CY05627/article/details/87563075