笔记就 懒得搞得有多好看了 自己看的顺就行
贴个luogu链接 uva比较慢 UVA12558 埃及分数 Egyptian Fractions (HARD version)
一开始看着紫书写的 就是添加了个约束条件 现在理解了之后搞一个
题目是搜索 考虑BFS 状态数太多 一次要向不知道数量个状态转移 不行
考虑DFS 解决了状态数过多的问题 但是深度太深了 也不行
所以就IDDFS解决 每次把a/b减去一个1/c 直到到达深度限制且最后减去的为埃及分数并且分母不受限
代码
#include <bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
using std::cin;
using std::cout;
using std::endl;
std::map<ll, int>map; //利用map来判断能不能用
ll ans[105], tmp[105]; //存答案
ll gcd(ll a, ll b) {if(a < b) std::swap(a, b); return b == 0 ? a : gcd(b, a % b);}
//不知道static_cast和(int)()有什么区别 不过看着很吊就是了
ll get_first(ll a, ll b) {return static_cast<int>(b / a) + 1;} //得到1/c <= a/b的c的最大值 由1/c <= a/b可推导出b <= a * c 进而推导出b / a <= c
bool check(int dep) { //判断最优解
for(int i = dep; i >= 0; i--)
if(ans[i] != tmp[i]) return ans[i] == -1 || ans[i] > tmp[i]; //要求的是分母最小 ans[i] == -1判断
return false;
}
void replace(int dep) {for(int i = 0; i <= dep; i++) ans[i] = tmp[i];}
bool IDDFS(int dep_lim, int dep, ll up, ll down, ll min) { //dep_lim为深度限制 dep为当前深度 up为剩下的分子 down为剩下的分母
bool flag = false; //因为是最优解 而第一个搜到的不一定是最优解 所以不能够搜到直接return
if(dep >= dep_lim) { //深度大于等于限制是就需要判断了
if(down % up) return false; //因为埃及分数的分子只能为1(然而那题题面没写)所以如果分子不是1就不是答案
tmp[dep] = down / up; //得到分母 存入答案
if(map.count(tmp[dep])) return false; //如果分母不能用就不是
if(check(dep)) replace(dep); //更新最优解
return true;
}
for(int i = std::max(min, get_first(up, down)); ; i++) { //因为最后得出的那个的分母是递增的 所以遍历下界的第一个条件为上次的分母,也就是min 另外一个条件为1/c <= a/b时c的最小值,因为如果1/c > a/b,那么就不会有结果
if(down * (dep_lim - dep + 1) <= up * i) break; //如果剩下几个全为1/i,加起来也不超过up/down就直接break,因为随着i的增大1/i只会越来越小,由(dep_lim - dep + 1) * (1 / i) < up / down可推导出
if(map.count(i)) continue; //如果i为受限的分母 就不能选
tmp[dep] = i; //存答案
ll b = i * down; //由up/down - 1/i = a/b可得出 up/down - 1/i = a/b --> (up * i)/(down * i) - down/(i * down) = a/b --> a = up * i - down, b = down * i
ll a = up * i - down;
ll g = gcd(a, b); //gcd
if(IDDFS(dep_lim, dep + 1, a/g, b/g, i + 1)) flag = true;
}
return flag;
}
int main() {
int t;
scanf("%d", &t);
int it = 1;
while(t--) {
int a, b, k;
memset(ans, -1, sizeof(ans));
memset(tmp, -1, sizeof(tmp));
map.clear();
scanf("%d%d%d", &a, &b, &k);
for(int i = 0; i < k; i++) {
ll tmp;
scanf("%lld", &tmp);
map[tmp] = 1;
}
int dep = 0;
while(1) {
if(IDDFS(dep, 0, a, b, get_first(a, b))) break;
dep++;
}
printf("Case %d: %d/%d=", it++, a, b);
for(int i = 0; i < dep; i++) printf("1/%d+", ans[i]);
printf("1/%d\n", ans[dep]);
}
system("pause");//上交oj时记得注释或删除
return 0;
}
不知道cnblogs怎么用latex就没用 一些细节也都标出来了 反正也是给自己看
#include <bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
using std::cin;
using std::cout;
using std::endl;
std::map<ll, int>map;
ll ans[105], tmp[105];
ll gcd(ll a, ll b) {if(a < b) std::swap(a, b); return b == 0 ? a : gcd(b, a % b);}
ll get_first(ll a, ll b) {return static_cast<int>(b / a) + 1;}
bool check(int dep) {
for(int i = dep; i >= 0; i--)
if(ans[i] != tmp[i]) return ans[i] == -1 || ans[i] > tmp[i];
return false;
}
void replace(int dep) {for(int i = 0; i <= dep; i++) ans[i] = tmp[i];}
bool IDDFS(int dep_lim, int dep, ll up, ll down, ll min) {
bool flag = false;
if(dep >= dep_lim) {
if(down % up) return false;
tmp[dep] = down / up;
if(map.count(tmp[dep])) return false;
if(check(dep)) replace(dep);
return true;
}
for(int i = std::max(min, get_first(up, down)); ; i++) {
if(down * (dep_lim - dep + 1) <= up * i) break;
if(map.count(i)) continue;
tmp[dep] = i;
ll b = i * down;
ll a = up * i - down;
ll g = gcd(a, b);
if(IDDFS(dep_lim, dep + 1, a, b, i + 1)) flag = true;
}
return flag;
}
int main() {
int t;
scanf("%d", &t);
int it = 1;
while(t--) {
int a, b, k;
memset(ans, -1, sizeof(ans));
memset(tmp, -1, sizeof(tmp));
map.clear();
scanf("%d%d%d", &a, &b, &k);
for(int i = 0; i < k; i++) {
ll tmp;
scanf("%lld", &tmp);
map[tmp] = 1;
}
int dep = 0;
while(1) {
if(IDDFS(dep, 0, a, b, get_first(a, b))) break;
dep++;
}
printf("Case %d: %d/%d=", it++, a, b);
for(int i = 0; i < dep; i++) printf("1/%d+", ans[i]);
printf("1/%d\n", ans[dep]);
}
//system("pause");//上交oj时记得注释或删除
retu