大体题意:
在普通的埃及分数上加了 几个限制, 多给了你K个数,让你求出最后的埃及分数结果中分母不能是K个数的某个数的结果。
思路:
先记录一下 埃及分数算法:
利用了迭代加深的搜索方式
最后的分母结果记录在ans数组中,最后只需要输出ans 数组即可!
首先将ans数组初始化为-1,表示没赋过值,这样在更新答案时,可以判断ans[i] == 1或者 v[i] < ans[i] 这样可以更新ans数组!
在dfs 函数中,有一个深度d 到达maxd时 进行判断返回,还有一个int from 参数,表示枚举的分母从哪里开始,然后就是目标分子和分母了!
然后d 到达maxd时,判断目标分数是不是埃及分数 和此时是不是更优解。 如果不是埃及分数直接return false了,最后return true;
如果没到达maxd,则先更新一下枚举分母的起源,然后就是剪枝,都合法时算出下一个分数,在进行dfs即可!
该题目多的限制 要在两个地方进行判断,一个是dfs 没到达maxd时在枚举分母时,发现i 时禁止的 直接continue;
另一个地方时 到达maxd时要判断更新的ans 是不是禁止的!、
具体详见代码和书籍!
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fin freopen("cin.txt","r",stdin);
#define fout freopen("out.txt","w",stdout);
using namespace std;
typedef long long ll;
const int maxn = 10000 + 10;
int maxd,k;
int Not[maxn];
ll v[maxn],ans[maxn];
int get_first(int a,int b){
return b/a+1;
}
ll gcd(ll a,ll b){
return !b?a:gcd(b,a%b);
}
bool forbid(int n){
for (int i = 0; i < k; ++i)if (Not[i] == n)return true;
return false;
}
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;
v[d] = bb/aa;
if (forbid(bb/aa))return false;
if (better(d))memcpy(ans,v,sizeof(ll) * (d+1));
return true;
}
bool ok = false;
from = max(from,get_first(aa,bb));
for (int i = from;;++i){
if (forbid(i))continue;
if (bb * (maxd-d+1) <= i * aa)break;
v[d] = i;
ll b2 = i*bb;
ll a2 = i*aa-bb;
ll g = gcd(a2,b2);
if (dfs(d+1,i+1,a2/g,b2/g))ok=1;
}
return ok;
}
int main(){
// fin;fout;
int T,cnt = 0;
scanf("%d",&T);
while(T--){
int a,b;
scanf("%d%d%d",&a,&b,&k);
memset(ans,-1,sizeof ans);
for (int i = 0; i < k; ++i)scanf("%d",&Not[i]);
int ok = 0;
for (maxd = 1;;++maxd){
if (dfs(0,get_first(a,b),a,b)){
ok = 1;
break;
}
}
printf("Case %d: %d/%d=",++cnt,a,b);
for (int i = 0; i <= maxd; ++i){
if (i)printf("+");
printf("1/%lld",ans[i]);
}
printf("\n");
}
return 0;
}