题目大意:给你一个分数$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 ight ceil$,否则就剩余负数了
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 }