题目链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=842&page=show_problem&problem=4003
题目大意:
把一个真分数表示成一系列埃及分数的和,要求项数最少,且最小分数值最大以此类推。并且给出
k(≤5)
个数,不能选用
分析:
对于这种没有搜索上限的题最好采用迭代加深搜索,设置一个上限
maxd
,
上限
maxd
还可以用来剪枝,当扩展到第
i
层时,若剩余值为
wrong了很多次才发现bug的代码。。
最初的bug:考虑最后一个值即
b/a
时未考虑
b/a
是否在给出的K个数内
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define MAXN 100000
using namespace std;
typedef long long LL;
LL v[MAXN],ans[MAXN];
LL maxd;
LL a ,b,k,K[10];
LL gcd(LL a,LL b)
{
return b==0?a:gcd(b,a%b);
}
bool better(LL d)
{
for(LL i=d ; i>-1; --i)
if(v[i]!=ans[i])return ans[i] == -1||v[i]<ans[i];
}
bool dfs(LL d,LL from,LL a,LL b)
{
if(d == maxd)
{
if(b%a)return false;
bool fk = false;
if(b/a<=1000)
{
for(int j = 0 ; j<k ; ++j)
if(K[j] == b/a)
{
fk = true;
break;
}
}
if(fk)return false;
v[d] = b/a;
if(better(d))memcpy(ans,v,sizeof(LL)*(d+1));
return true;
}
from = max(from,b/a+1);
bool ok = false;
for(LL i = from ; ;i++)
{
bool fk = false;
if(b*(maxd+1-d)<=a*i)break;
if(i<=1000)
{
for(int j = 0 ; j<k ; ++j)
if(K[j] == i)
{
fk = true;
break;
}
}
if(fk)continue;
//i合法,计算剩余分数
v[d] = i;
LL bb = b*i;
LL aa = a*i-b;
LL g = gcd(aa,bb);
if(dfs(d+1,i+1,aa/g,bb/g)){ok = true;}
}
return ok;
}
int main()
{
//freopen("H:\\c++\\file\\stdin.txt","r",stdin);
int T;
scanf("%d",&T);
int kase = 0;
while(T--)
{
scanf("%lld %lld %lld",&a,&b,&k);
for(int i=0 ; i<k ; ++i)scanf("%lld",&K[i]);
for(maxd = 1 ; ;++maxd)
{
memset(ans,-1,sizeof(ans));
if(dfs(0,b/a+1,a,b)){break;}
}
printf("Case %d: ",++kase);
printf("%lld/%lld=",a,b);
for(int i=0 ; i<maxd ; ++i)
printf("1/%lld+",ans[i]);
printf("1/%lld",ans[maxd]);
putchar('\n');
}
return 0;
}