题目:http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=358
题意:给你个真分数,你需要将其化简为最少的若干特殊真分数之和,你要输出这个序列(序列按递增序)。如果有不同的方案,则分数个数相同的情况下使最大的分母最小。若还相同,则使次大的分母最大……以此类推。如:2/3=1/2+1/6,但不允许2/3=1/3+1/3,因为加数中有相同的。对于一个分数a/b,表示方法有很多种,但是哪种最好呢? 首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好。
分析:此题目解答树宽度和深度都没有上限,不能直接用bfs或者dfs。
解决方案是采用迭代加深搜索:从小到大枚举深度上限。
迭代加深搜索,实质上是限定下界的深度优先搜索。即首先允许深度优先搜索K层,若没有发现可行解,再将K+1后
重复以上步骤搜索,直到搜索到可行解。
在迭代加深搜索的算法中,连续的深度优先搜索被引入,每一个深度约束逐次加1,直到搜索到目标为止。这样可以
看出重复搜索了好多。但是它的好处在于:
1.空间开销小:每个深度下实际上是一个深度优先搜索,不过深度有限制,而DFS的空间消耗小是众所周知的。
2.利于深度剪枝。
3.时间效率不低:虽然重复搜索,但是大家不难理解,前一次搜索跟后一次相不是微不足到的。
此题中,深度上限还可以用来“剪枝”。按照分母递增的顺序来进行扩展,如果扩展到第i层时,前i个分数之和为c/d,而第i个分数为1/e,则接下来至少还需要(a/b-c/d)(1/e)个分数,综合才能达到(a/b)。
<pre name="code" class="cpp">//迭代加深搜索
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cassert>
using namespace std;
int a,b,maxd;
//约分
long long yf(long long a,long long b){
return b==0?a:yf(b,a%b);
}
// 返回满足1/c <= a/b的最小c
int find_first(long long a,long long b) {
return b/a+1;
}
const int maxn=100+5;
long long v[maxn],ans[maxn];
// 如果当前解v比目前最优解ans更优,更新ans
bool better(int d){
for(int i=d;i>=0;i--){
if(v[i]!=ans[i]) {
return ans[i]==-1||v[i]<ans[i];
}
}
return false;
}
// 当前深度为d,分母不能小于from,分数之和恰好为aa/bb
bool dfs(int d,int from,long long aa,long long bb){
if(d==maxd) {//判断是否有解
if(bb%aa) return false; // aa/bb必须是埃及分数
v[d]=bb/aa;
if(better(d)){
memcpy(ans,v,sizeof(long long)*(d+1));
}
return true;
}
bool ok=false;
from=max(from,find_first(aa,bb)); // 枚举的起点
for(int i=from;;i++) {
// 剪枝:如果剩下的maxd+1-d个分数全部都是1/i,加起来仍然不超过aa/bb,则无解
if(bb*(maxd+1-d)<=i*aa){
break;
}
v[d]=i;
// 计算aa/bb - 1/i,设结果为a2/b2
long long b2=bb*i;
long long a2=aa*i-bb;
long long g=yf(a2,b2); // 以便约分
if(dfs(d+1,i+1,a2/g,b2/g)){
ok=true;//如果有解就将ok的值改为true,从而判断是否有解
}
}
return ok;
}
int main()
{
int kase=0;//统计次数
while(cin>>a>>b){
int ok=0;
for(maxd=1;maxd<=100;maxd++){
memset(ans,-1,sizeof(ans));//将答案数组清空
if(dfs(0,find_first(a,b),a,b)){//通过迭代加深搜索来寻求答案
ok = 1;
break;
}
}
cout<<"Case "<<++kase<<": ";
if(ok){
cout<<a<<"/"<<b<<"=";
for(int i=0;i<maxd;i++){
cout<<"1/"<<ans[i]<<"+";
}
cout<<"1/"<<ans[maxd]<<"\n";
}
else{
cout<<"No solution.\n";
}
}
return 0;
}