埃及分数 解题报告

【杂言】:

我是真的不想写了,但是,我挂了,挂的很彻底,30分钟找思路,1个小时修了三遍码,全挂了,被迫看题解,我真是醉了,一到分母,则为坟墓,(可能上天不想让我充当别人的分母吧)\(UVA\)的题,真的太狗了,好倒是挺好,这个输出就 ,,,算了,不吐槽了,但是我看了题解,看了题解就要写解题报告,这是习惯。


【题目描述】:

给定一个分数 \(\dfrac{a}{b}\),然后给定 \(k\) 个数 \(q_i\)
要求把 \(\dfrac{a}{b}\) 分为几个不同的分子为 \(1\) 的最简分数,要求分成的分数的分母中不能出现 \(q_i\)
本题有 \(t\) 组数据, \(t \le 100\)
其他数据范围: \(2 \le a < b \le 876\)\(0 \le k \le 5\)\(\gcd(a,b)=1\)\(2 \le q_i \le 1000\)


【思路分析】:

想想,对于分数的分解问题,有没有好的数学方法去解决,因为往往数学能够解决的,信息中也就太简单了, 显然, 数学中也没有什么好方法,看到了 数据总共也不是很大,那么也就是只能搜索了,

对于搜索,朴素的搜索我写挂了(@ο@) ~, 悲伤的事情,想起就是难过的经历。

至于这道题的朴素写法, 那就是枚举有关(这里姑且说为有关)的分母,然后对于每一个分母相加起来,看看是否是可以与给定的数值相等, 如果可以,那就进行比较,同时确保这一个分母可以用和比较是否用这个分母是最优的 。 --- 这就是最朴素的搜索思路了。

然后便是有关分母的枚举上界和下界问题 , 总不能枚举到 \(10^9\)的枚举吧

有关其下界问题: 其下界也就是上一个用到的分母加 1 和 $\frac{b \times y}{ b \times x \ - \ a \times y} $ 的最大值;

解释一下 :
上一次用到的分母加 1 , 不必说明了 。
$\frac{b \times y}{ b \times x \ - \ a \times y} $的推导,
我们设其上一个字母为 \(kkk\)
也就是说 \(\frac{a}{b}\) + \(\frac{1}{kkk}\) \(\leq \ \frac{x}{y}\) , 然后化简,不会可以找小学老师了(逃) ,
有关其上界问题
设的变量 ,$d=现在迭代的次数 \ \ dep是现在迭代到的数 $ ,
那么\(max = \frac{\frac{x}{y} \ - \ \frac{a}{b} }{d - dep}\) ,很显然 , 为了我们的精度, 我们可以转化为\(\times\)的形式;
即为 : \(b \times y \times (d - dep) + a \times max \times >= x \times b \times max\) ,相当于一个终止条件嘛 , 也没必要去在意值。(值是你枚举的,管他干嘛?)


【注意一下】:

  1. 很明显,我们选用 \(set\)去判别有没有这个值是更优的 (以\(log_2 n\)的)
  2. 更为明显, 分数必须要约分 , 不约分等于白做
  3. 他的输入输出是很毒瘤的 , 不能就随便输出的
  4. 不要忘记\(set\)迭代器转化为\(int\)类型 , 不然一直\(CE\)

【Code】:

暂且先放下代码,日后再补上,毕竟要去考试了呀~

\(TM\)考试考炸了

#include <iostream>
#include <cstdio>
#include <cstring>
//#include <algorithm> //打死我也没想到,algorithm我竟然定义了其中的关键词,为了解决这个问题,我决定,不用他,哈哈
#include <cmath>
#include <cstdlib>
#include <queue>
#include <set>
#define inf 0x3f
#define int long long
#define QwQ printf("运行过了") ;
using namespace std ;
const int maxn = 1e5 ;
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)){ if(ch =='-') f = - 1 ; ch = getchar() ; }
	while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar() ; }
	return x * f ;
}
int gcd(int x,int y)
{
	return !y?x:gcd(y,x%y);
}
int lcm(int x , int y)
{
	return x / gcd(x,y) * y;  
}
//分数相减通分公式(a*x-b)/b*x
int x , y , k , dep , tot = 0;
bool vis[maxn] ;
set<int> s ;
vector<int> now ,ans ;
void check()
{
	if(ans.empty() ||ans.size() > now.size())
	{
		ans = now ;	
		return ;
	} 
	if(now.size() > ans.size() ) return ;
	for(int i = (int)now.size() - 1 ; i >= 0 ; i--)
	{
		if(now[i]<ans[i])
		{
			ans = now ;
			return ;
		}
		else if(now[i] > ans[i] )
		{
			return ;
		}
	}
	return ;
} 
void prepare() // 多组数据每次都进行初始化 
{
	now.clear() ;
        ans.clear() ;
	memset(vis , false ,sizeof(vis)) ;
	s.clear() ;
	return ;
}
void search(int d , int last , int a , int b) 
{
	if(a * y > b * x) 
	{	
		return ;
	}
	if(d >= dep)
	{
		if( a * y == b * x) check() ;
		return ;
	}
	int p = gcd(a, b) ;
	a /= p , b /= p ;
	int head = max(last , y * b / (x * b - y *a) ) ;
	for(int i = head ; b * y * (dep - d ) + a * i * y >= x * b * i ; i++)
	{
		if(s.count(i)) continue ;
		now.push_back(i) ;
		search(d + 1 , i + 1 , a * i + b , b * i) ;
		now.pop_back() ;
 	}
 	return ;
}
void work()
{
	prepare() ;
	x = read() , y = read() , k = read() ;
	while(k--)
	{
		int m = read() ;
		s.insert(m) ;
	} 
	int gcd_ = gcd( x , y ) ;
	x /= gcd_ , y /= gcd_ ;
	for(dep = 1 ; ans.empty() ; dep ++)
	{
		search(0 , 2 , 0 , 1) ; 
	} 
	printf("Case %lld: %lld/%lld=" , ++tot , x , y ) ;
	printf("1/%lld", ans[0]) ;
	for(int i = 1 ; i < (int)ans.size() ; i++)
	{
		printf("+1/%lld" , ans[i]) ;
	}
	printf("\n") ; 
	return ;
} 
signed main()
{
	int T = read() ;
	while(T--)
	{
		work() ;
	}
	return 0;
}
posted @ 2020-12-20 22:09  SkyFairy  阅读(131)  评论(1编辑  收藏  举报