这道题做的我 七窍生烟 半死不活 怒斥UVA无人性
明白了longlong 的重要性
题目大意
给一个真分数a/b,把他拆分成1/x1+1/x2+1/x3……
若有多种情况,输出拆分出分数最少的一种
如果还有多种情况,输出分母第一大最小,如果还不行,第二大最小,以此类推。
第一行输入数据组数 t
注意,每行输入这几个数 :
分子a 分母b 不能用的分母k个 然后后面会有k个不能用给的整数
解题过程
被卡了两个半小时,代码越来越题解化。
不过下面那个题解没说的太清楚,这里附上几个证明。
证明1. 分母下界取值
∵ a/b 为真分数 ,a,b,k均为整数
∴ a<b
设正整数x,y ax+y=b
发现 floor(b/a)=floor( (x * a+y) /a )=floor(x * a/a)+floor(y/a) = x
以及 floor(b/a)<=b/a
∴ floor(b/a)=x
∴ x+1 > b/a , ∴floor(b/a)+1>b/a
∴ 1/(x+1) < a/b 又 1/floor(b/a)=1/x >=a/b
∴ x+1就是最小的 k 使得 1/k < a/b
∴ k=floor(b/a) + 1
证明2. 分母上界取值
if(a * i>=b * (mmxdep-dep+1)) return ;
证明过程
a * i >= b * (mmxdep-dep+1)
a >= (b * mmxdep-dep+1)/i 两边除i
a/b >= (mmxdep-dep+1) /i 两边除b
a/b >= (1/i ) * (mmxdep-dep+1) 换一个等价写法
也就是说,剩下即使按照每一个分数都是比最大还大的i/1会留下,因此直接剪枝
通分的方式
这句话在通分: na=a * i-b,nb=b * i;
当前数值为a/b,我们要从中抽出1/i
原式 = a/b-1/i
=ai/bi-b/bi
=(ai-b)/bi
na存的是左边分子,nb存的是右边分母,这个过程相当于通分
我觉得应该差不多了,那就上代码吧
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| g#include<iostream> #include<cmath> #include<cstdio> #include<cstring> #include<set>
#define re int
using namespace std;
long long n,t,k,mmxdep,f; long long bs[100500],tmp[100500],mutia,mutib,w[100500];
set <long long> p;
int pd() { for(re i=mmxdep;i>=1;i--) { if(bs[i]!=w[i]) { if(bs[i]==0) return 0; if(bs[i]>w[i]) return 0; return 1; } } return 1; }
long long gcd(long long a,long long b) { if(b==0) return a; else return gcd(b,a%b); }
void dfs(int dep,long long a,long long b,long long lasi) { long long i; if(dep==mmxdep) { if(b%a||p.count(b/a)) return ; w[dep]=b/a; if(!pd()) memcpy(bs,w,sizeof(w)); f=1; return ; } for(i=max(lasi,b/a+1);;i++) { if(p.count(i)) continue; if(a*i>=b*(mmxdep-dep+1)) return ; long long na=a*i-b,nb=b*i; long long fri=gcd(na,nb); w[dep]=i; dfs(dep+1,na/fri,nb/fri,i+1); } return ; }
int main() { int i,j; scanf("%lld",&t); for(i=1;i<=t;i++) { long long t1; p.clear(); scanf("%lld %lld %lld",&mutia,&mutib,&k); for(j=1;j<=k;j++) scanf("%lld",&t1),p.insert(t1); for(mmxdep=2;;mmxdep++) { memset(bs,0,sizeof(bs)); dfs(1,mutia,mutib,mutib/mutia+1); if(f) break; } printf("Case %d: %lld/%lld=1/%lld",i,mutia,mutib,bs[1]); for(j=2;j<=mmxdep;j++) printf("+1/%lld",bs[j]); printf("\n"); memset(bs,0,sizeof(bs)); f=0; } return 0; }
|