一些列舉題目(二)
阿新 • • 發佈:2020-12-14
埃及分數(Eg[y]ptian Fractions (HARD version), Rujia Liu's Present 6, UVa12558)
把a/b寫成不同的埃及分數之和,要求項數儘量小,在此前提下最小的分數儘量大,然後第二小的分數儘量大……另外有k(0≤k≤5)個數不能用作分母。例如,k=0時\(5/121=1/33+1/121+1/363\),不能使用33時最優解為\(5/121=1/45+1/55+1/1089\)。輸入保證\(2≤a<b≤876,gcd(a,b)=1\),且會挑選比較容易求解的資料。
輸入輸出
第一行輸入是T,下面T行每行是一組輸入示例,每行的前三個數是a,b,k。之後的k個數是不能用作分母的k個數。
Sample Input
5
2 3 0
19 45 0
2 3 1 2
5 121 0
5 121 1 3
Sample Output
Case 1: 2/3=1/2+1/6
Case 2: 19/45=1/5+1/6+1/18
Case 3: 2/3=1/3+1/4+1/12
Case 4: 5/121=1/33+1/121+1/363
Case 5: 5/121=1/45+1/55+1/1089
思路
之前做埃及分數的時候,由於這是紫書裡介紹迭代加深的第一道題,在當時不瞭解迭代加深的原理再加上這題這麼噁心的情況下,沒咋明白。這回明白了。
思路詳見程式碼註釋
程式碼
#include "iostream" #include "cstdio" #include "cstring" #include "cmath" #include "set" #define LL long long int #define MAXK 5 #define MAXCS 100 using namespace std; LL a, b,k,cs[MAXCS],ans[MAXCS]; set<int> ks; int cnt; LL gcd(LL a, LL b) { return b == 0 ? a : gcd(b, a % b); } /* 找到第一個滿足1/c<a/b的c 1/c<a/b c<b/a 所以c為b/a向上取整或者b/a+1 */ LL first_smaller_than(LL aa, LL bb) { return ceil(bb / (double)aa); } /* 因為題目中要求的是找到項數儘量小,最小的分數儘量大,第二小的分數儘量大... 所以先找到的答案可不一定是正確的,還得在此深度下繼續查詢 better判斷當前的答案是否比已有答案更好 */ bool better(int maxd) { for (int i = maxd ; i >= 0; i--) if (cs[i] != ans[i])return ans[i]==-1||cs[i] < ans[i]; return false; } /* 之前選擇的是選擇第d個1/c 最大選擇maxd個1/c 當前離總目標a/b還差aa/bb */ bool dfs(int d,int maxd,LL first,LL aa, LL bb) { // 如果深度等於最大深度 if (d == maxd) { // 如果最後一個滿足1/x的形式並且不在ks中 if (bb % aa || ks.count(bb / aa))return false; cs[d] = bb / aa; // 如比當前答案更好 更新 if (better(maxd)) { memcpy(ans, cs, sizeof(cs)); return true; } return false; } first = max(first,first_smaller_than(aa,bb)); bool ok = false; // 往後依次遍歷每一個c for (int c = first;; c++) { // 如果c在ks裡,不要 if (ks.count(c) != 0)continue; // 如果後面的有限個都用1/c還是湊不夠aa/bb,剪枝 if (bb*(maxd+1-d)<=aa*c) break; cs[d] = c; // 算新的aabb,並通分 LL naa=aa*c-bb, nbb = bb*c; LL g = gcd(nbb, naa); if (dfs(d + 1, maxd,c+1, naa/g, nbb/g))ok = true; } return ok; } int main() { int T; scanf("%d", &T); for (int kase = 1; kase <= T; kase++) { scanf("%lld %lld %d", &a, &b, &k); ks.clear(); for (int i = 0; i < k; i++) { int tmp; scanf("%d", &tmp); ks.insert(tmp); } for (int maxd = 1;; maxd++) { memset(ans, -1, sizeof(ans)); cnt = 0; if (dfs(0, maxd,first_smaller_than(a,b), a, b)) { printf("Case %d: %lld/%lld=", kase, a, b); for (int j = 0; j <= maxd; j++) { printf("1/%lld", ans[j]); if (j < maxd)printf("+"); } printf("\n"); break; } } } return 0; }