codevs 1288 埃及分数 (迭代加深搜索)

题目大意:给你一个分数$a/b$,把它拆解成$\sum_{i=1}^{n}1/ai$的形式,必须保证$ai$互不相同的情况下,尽量保证n最小,其次保证分母最大的分数的分母最小

什么鬼玄学题!!!

因为要保证$n$最小,所以从小到大遍历最大层数$n$,令$a'/b'$表示当前剩余的需要被拆解的数

如果当前分数的数量等于$n$,更新最优解并返回,继续搜总层数为$n$的所有情况,然后输出答案,因为这一层的最优解一定是全局最优解

每次选择一个数$i$,表示当前层的数选择了$i$,把剩余的数改成$a'/b'-1/i$进入下一层

显然暴力枚举$i$会$T$,那就加一些玄学的剪枝

1.i要大于等于$\left \lceil b/a \right \rceil$,否则就剩余负数了

2.为了减少枚举次数,可以保证前几层选择的数升序排列,所以$i$要大于$now[dep-1]$

3.$(n-dep)/i+1/i>=a'/b'$,因为后几层选择的数都小于等于$1/i$,所以后几层和的最大值要大于等于$a'/b'$

有了这几个玄学的剪枝,我们就能以$O($能过$)$的复杂度通过本题!

 1 #include <map>
 2 #include <queue>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <algorithm>
 6 #define NN 5010
 7 #define MM 2000
 8 #define ll long long 
 9 #define uint unsigned int
10 #define ull unsigned long long 
11 using namespace std;
12 
13 ll A,B;
14 int K=500;
15 ll now[NN],ans[NN];
16 ll gcd(ll x,ll y){if(!y)return x;return gcd(y,x%y);}
17 const ll maxn=666666666666ll;
18 void dfs(int dep,ll a,ll b,int ma)
19 {
20     if(dep==ma+1){
21         if(a>0) return;
22         if(now[ma]<ans[ma])
23             for(int i=1;i<=ma;i++)
24             ans[i]=now[i];
25         return;
26     }
27     ll na,nb,g;
28     for(ll i=max(now[dep-1]+1,(b/a)+(b%a==0?0:1));a*i<=(ma-dep+1)*b;i++){
29         na=a*i-b,nb=b*i;
30         g=gcd(na,nb);
31         now[dep]=i;
32         dfs(dep+1,na/g,nb/g,ma);
33         now[dep]=0;
34     }
35 }
36 
37 int main()
38 {
39     //freopen("t2.in","r",stdin);
40     scanf("%lld%lld",&A,&B);
41     for(int k=1;k<=K;k++)
42         ans[k]=maxn;
43     for(int k=1;k<=K;k++)
44     {
45         dfs(1,A,B,k);
46         if(ans[k]<maxn){
47             for(int i=1;i<=k;i++)
48                 printf("%lld ",ans[i]);
49             puts("");
50             break;
51         }
52     }
53     return 0;
54 }

猜你喜欢

转载自www.cnblogs.com/guapisolo/p/10003611.html