7.6 迭代加深搜索_埃及分数问题

bala

从一开始的一头雾水,完全不懂如何下手。接着认真再看了紫书、博客,还是不知道怎么下手。期间尝试过跟着别人的代码敲,但,完全不懂他们的思路。

本着实践出真知的精神,于是我!!!决定从一个小规模的、比原题简单的问题入手,搞定一个小规模问题,那说不定就能弄懂原题了,照葫芦画瓢嘛。

于是我纠结的问题就缩小成:输入真分数 a / b,求加数个数为3的情况下的埃及分数解,即把 b / a 表示成 1 / m + 1 / n + 1 / p

balabala

从早上九点到晚上八点,终于有点突破咯,感觉快要解决咯,哈哈,有点小激动,记录一下。
——2020/4/18 20:30
宁看,
在这里插入图片描述
在茫茫可选答案中找到了正确答案,哈哈哈哈哈,这波,这波是
在这里插入图片描述
——2020/4/18 20:40

思路

理一下我的头绪

  1. 采用递归算法(应该算深度优先搜索DFS)
  2. 对递归深度进行枚举
    eg. 深度maxd = 3,代表用3个加数来表示 a / b;
  3. 另外有个重要的函数是估价函数,即判断在什么情况下不可能在当前的深度限制中得出解。
    实现:剩下的分数若 > (下一层最大的分数 乘 剩下的深度),则 直接返回上一层即可,不可能得出解了。

注: 其实要是真的不理解,可以试试解决问题:输入真分数 a / b,求加数个数为3的情况下的埃及分数解,即把 b / a 表示成 1 / m + 1 / n + 1 / p;
真的是可以从这个小问题入手,举一反三,最后解决这个问题的!

最终代码

客官若要复制我的代码看的话,建议把一些注释删了,注释部分是我调试过程中留下的,hh,虽然可能没人看,但是,万一有呢?

//对于整个问题,一头雾水 
// 方向________________>>>>>>>>> 
//那就先弄懂小规模问题: a / b 分解 成三个埃及分数。列出所有情况。 
//2020/4/18    
//9:00 -  21:30
//@reasonbao 
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long int ll;

//比较 y1 / x1  与  y2 / x2 的大小 
int CompareFraction(ll y1, ll x1, ll y2, ll x2) {
	ll fraction_front = y1 * x2, fraction_next = y2 * x1;	//通分交叉相乘,乘积可能非常大
	if (fraction_front == fraction_next) return 0;
	else if (fraction_front > fraction_next) return 1;
	else return 2;
}

//估价函数
bool Assess(ll fenzi, ll fenmu, ll layer, ll biggest) {
	if (CompareFraction(fenzi, fenmu, layer, biggest) == 2  || CompareFraction(fenzi, fenmu, layer, biggest) == 0)
		return true;		//"假想最大值",至少应该要比剩余的分数大或等于 
	return false;		    //若剩余层数 * 最大埃及分数  < 剩余的分数," 剪枝 " 
} 
 

//最大公约数,用于约分 
ll Gcd(ll a, ll b) {
	if (b == 0) return a;
	else return Gcd(b, a % b); 
}

//约分
void Reduce(ll &fenzi, ll &fenmu) {
	if (fenzi == 0) {
		fenzi = 0;
		fenmu = 1;
	}
	else {
		ll gcd = Gcd(fenzi, fenmu);
		fenzi /= gcd;
		fenmu /= gcd;
	}
} 

void Sub(ll &y1, ll &x1, ll y2, ll x2) {
	ll fraction_front = y1 * x2, fraction_next = y2 * x1;	//通分交叉相乘,乘积可能非常大
	x1 = x1 * x2;
	y1 = fraction_front - fraction_next;
	Reduce(y1, x1);
}

ll  ans[1000];
ll  ans_best[1000];
bool g_is_ok = false;
 
//void Dfs(ll remain_fenzi, ll remain_fenmu, ll from, ll cur, ll maxd) {
//	
//	for (ll i = from; ; i ++ ) {	//向第cur层填入埃及分数,。 注:from初始化为1,即 1 / 1 
//		
//		while (CompareFraction(remain_fenzi, remain_fenmu, 1, i) == 2) {
			cout << "减之前:"; cout << remain_fenzi << "/" << remain_fenmu << " - " << 1 << "/" << i << endl;  
			cout << "标记1111\n";
//			i ++ ;	//直到找到该层可填的数 (条件:1 / i 比剩余分数 小 或者 等)
//		}
//		
//		ans[cur] = i;
//		cout << "减之前:"; cout << remain_fenzi << "/" << remain_fenmu << " - " << 1 << "/" << i << endl;
//		Sub(remain_fenzi, remain_fenmu, 1, i); 
//		cout << "减之后:"; cout << remain_fenzi << "/" << remain_fenmu << endl;
//		
//		if (remain_fenzi == 0) {
//			cout << "数组情况:————";
//			for (int m = 0; m < 10; m ++ ) cout << ans[m] << " "; cout << endl << endl;
//		} 
//		 
//		Dfs(remain_fenzi, remain_fenmu, i + 1, cur + 1, maxd);
//		cout << "标记222\n";
//	}
//	cout << "标记333\n";
//} 

//void Dfs(ll remain_fenzi, ll remain_fenmu, ll from, ll cur, ll maxd) {
//	
//	if (cur == maxd ) {
//		if ( ! remain_fenzi) {
//			cout << "数组情况:————";
//			for (int m = 0; m < 10; m ++ ) cout << ans[m] << " "; cout << endl << endl;
//		}
//		return;
//	}
//	
//	for (ll i = from; ; i ++ ) {	//向第cur层填入埃及分数,。 注:from初始化为1,即 1 / 1 
//	
//		if (! Assess(remain_fenzi, remain_fenmu, maxd - cur, i)) return;	//不可能事件,返回上一层 (上一层尝试其他数),不知道这算不算 ""剪枝"" 
//		
//		while (CompareFraction(remain_fenzi, remain_fenmu, 1, i) == 2) {
//			i ++ ;	//直到找到该层可填的数 (条件:1 / i 比剩余分数 小 或者 等)
//		}
//		
//		ans[cur] = i;
//		cout << "减之前:"; cout << remain_fenzi << "/" << remain_fenmu << " - " << 1 << "/" << i << endl;
//		
//		ll remain_fenzi_tmp = remain_fenzi;
//		ll remain_fenmu_tmp = remain_fenmu;
//		
//		Sub(remain_fenzi_tmp, remain_fenmu_tmp, 1, i); 
//		
//		cout << "减之后:"; cout << remain_fenzi_tmp << "/" << remain_fenmu_tmp << endl;
//		
//		Dfs(remain_fenzi_tmp, remain_fenmu_tmp, i + 1, cur + 1, maxd);
//	}
//} 

//比较相同深度情况下的更优解
void Better(ll pos) {
	if ( !ans_best[pos] || ans[pos] <= ans_best[pos]) {
		for (int i = 0; i <= pos; i ++ ) 
			ans_best[i] = ans[i];
	}
}

void Dfs(ll remain_fenzi, ll remain_fenmu, ll from, ll cur, ll maxd) {
	
	if (cur == maxd ) {
		if ( ! remain_fenzi) {
			cout << "数组情况:————";
			for (int m = 0; m < maxd; m ++ ) cout << ans[m] << " "; cout << endl << endl;
			Better(maxd - 1);
			g_is_ok = true;
		}
		return;
	}
	
	for (ll i = from; ; i ++ ) {	//向第cur层填入埃及分数,。 注:from初始化为1,即 1 / 1 
	
		if (! Assess(remain_fenzi, remain_fenmu, maxd - cur, i)) return;	//不可能事件,返回上一层 (上一层尝试其他数),不知道这算不算 ""剪枝"" 
		
		while (CompareFraction(remain_fenzi, remain_fenmu, 1, i) == 2) {
			i ++ ;	//直到找到该层可填的数 (条件:1 / i 比剩余分数 小 或者 等)
//			cout << "(((\n";
		}
		
		ans[cur] = i;
//		cout << "减之前:"; cout << remain_fenzi << "/" << remain_fenmu << " - " << 1 << "/" << i << endl;
		
		ll remain_fenzi_tmp = remain_fenzi;
		ll remain_fenmu_tmp = remain_fenmu;
		
		Sub(remain_fenzi_tmp, remain_fenmu_tmp, 1, i); 
		
//		cout << "减之后:"; cout << remain_fenzi_tmp << "/" << remain_fenmu_tmp << endl;
		
		Dfs(remain_fenzi_tmp, remain_fenmu_tmp, i + 1, cur + 1, maxd);
	}
} 

void OutputResult(ll a, ll b, ll maxd) {
	printf("%lld/%lld=", a, b);
	for (int i = 0; i < maxd; i ++ ) {
		if (i >= 1) printf("+");
		printf("%lld/%lld", 1, ans_best[i]);
	}
	cout << endl;
}

int main() {
//	ll a,b,c,d;
//	while (cin >> a >> b >> c >> d) {
		cout << CompareFraction(a,b,c,d) << endl;
//
		cout << Assess(a,b,c,d);
//
		cout << Gcd(a,b); 
//
		Reduce(a, b); Reduce(c, d);
		cout << a << " " << b << " " << c << " " << d << endl; 
//	
//		
//	}

//	memset(ans, 0, sizeof(ans));
//	memset(ans_best, 0, sizeof(ans_best));
//	Dfs(495, 499, 1, 0, 6);
//	cout << "RESULT: ";
//	for (int m = 0; m < 6; m ++ ) cout << ans_best[m] << " "; cout << endl << endl;

	ll a,b;
	while (cin >> a >> b) {
		for (ll maxd = 1; ; maxd ++ ) {	//对DFS的深度进行枚举,也就是限制深度(实现题目中:加数少的情况比多的好) 
			memset(ans, 0, sizeof(ans));
			memset(ans_best, 0, sizeof(ans_best));
			Dfs(a, b, 1, 0, maxd);

			if (g_is_ok)	{
				OutputResult(a, b, maxd);
				break;
			}
		}
		g_is_ok = false;	//记得重置 
	}
	
	
	return 0;
} 

这代码,我看出了紫书中 递归枚举排列 的影子,哈哈,果然还是有收获的,啃这本书!
END
临渊羡鱼,不如退而结网
——————————————
若有什么错误,欢迎大佬指正!
若有问题,也欢迎在评论提出!
——————————————

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值