知识点:迭代搜索,剪枝
题意:
给定一个假分数\(\frac{a}{b}\),构造一系列数满足
\[\sum_{i=1}^n\frac{1}{a_i}
\]
其中,每个\(a_i\)都是一个整数。所有解中,加数越少越优秀,相同加数数目,最小的加数越大越优秀,寻找最优秀的的解。
思路:
首先还是较为容易判断是搜素的,随即就来到一个边界问题。普通的搜索的话,不容易判定什么时候停止搜索。索性就提前限制搜索深度,实际上就变成了一个迭代搜索。新的体悟是,这种方法有点像二分答案,没有直达答案的方法,就一个个枚举,判断是否符合可能为答案。二分通过单调性节约时间,搜索通过剪枝。
剪枝主要是两点,每次匹配\(\frac{1}{a_i}\)和当前需要匹配数\(\frac{a}{b}\)都需要
\[\frac{1}{a_i}<\frac{a}{b}
\]
\[\frac{1}{a_i}*(limit-depth)>=\frac{a}{b}
\]
三式子中,limit为限制深度,depth为当前深度。
额外一些次要的剪枝见代码
题解:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<algorithm>
#define rep(i,l,n) for(int i=(l);i<=(n);++i)
#define ll long long
using namespace std;
ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
int fz,fm;
ll ans[1000],ans_len,tepans[1000],tepans_len;
void dfs(int cs,ll fz,ll fm,int limit)
{
if(cs>limit)return; //超过limit返回
if(fm%fz==0&&fm/fz>tepans[tepans_len]&&(ans_len==0||fm/fz<ans[ans_len]))
{
++tepans_len;
tepans[tepans_len]=fm/fz;
ans_len=tepans_len;
memcpy(ans,tepans,sizeof(ans));
--tepans_len;
return;
// 这是到达终点,记录答案返回,第一次到达终点不一定是最优答案。
}
ll i=fm/fz;
if(i<=tepans[tepans_len])i=tepans[tepans_len]+1;
//i的取值,满足式子2,3,同时也应当优于上次的最优解tepans
ll j=(limit-cs)*fm/fz;
while(i<=j)
{
if(ans_len>0&&i>=ans[ans_len])return;
int k=gcd(fm,i);
ll xfm=i*fm/k,xfz=fz*i/k-fm/k;
if(xfz<=0)
{
++i;
continue;
}
tepans[++tepans_len]=i;
dfs(cs+1,xfz,xfm,limit);
tepans_len--;
++i;
}
return ;
}
int main()
{
while(scanf("%d%d",&fz,&fm)!=EOF)
{
memset(ans,0,sizeof(ans));
memset(tepans,0,sizeof(tepans));
ans_len=0,tepans_len=0;
int limit=1;
while(limit)
{
dfs(1,fz,fm,limit);
if(ans_len)break;
++limit;
}
rep(i,1,ans_len)
{
if(i!=1)printf(" ");
printf("%lld",ans[i]);
}
printf("\n");
}
// system("pause");
return 0;
}
posted on
浙公网安备 33010602011771号