dfs+剪枝
题意:给定一个分数,问用分子为1的分数加和来构成这个分数有多少种方式。要求每种情况分数的个数不超过n,分母乘积不超过a。
分析:dfs+剪枝,记录当前剩余的需要构成的分数值,然后试图用所有的可以使用的名额平分这个剩余值,再试图用一个分数构成这个剩余值,这样就确定了我要枚举当前分母的范围。并随时检查所有分母的乘积是否超限。
View Code
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
usingnamespace std;
#define maxn 10
struct Partition
{
longlong a, b;
Partition(longlong aa, longlong bb) :
a(aa), b(bb)
{
}
Partition()
{
}
} par[maxn];
longlong ans;
longlong p, q, a, n;
booloperator<(const Partition &x, const Partition &y)
{
return x.a * y.b < y.a * x.b;
}
longlong gcd(longlong x, longlong y)
{
if (!x ||!y)
return x > y ? x : y;
for (longlong t; t = x % y; x = y, y = t)
;
return y;
}
Partition abstract(Partition a, Partition b)
{
Partition ret;
ret.b = a.b * b.b;
ret.a = a.a * b.b - b.a * a.b;
longlong g = gcd(ret.a, ret.b);
ret.a /= g;
ret.b /= g;
return ret;
}
void dfs(longlong t, Partition left, longlong sum)
{
if (left.b / left.a * sum > a)
return;
if (left.a ==1&& left.b >= par[t -1].b && sum * left.b <= a)
{
ans++;
}
if (t >= n)
return;
if ((n - t +1.0) * left.b / left.a < par[t -1].b)
return;
for (longlong i = max(par[t -1].b, left.b / left.a); sum * i <= a && (n - t +1.0) * left.b / left.a >= i; i++)
{
par[t].a =1;
par[t].b = i;
if (par[t] < left)
{
dfs(t +1, abstract(left, par[t]), sum * i);
}
}
}
int main()
{
//freopen("t.txt", "r", stdin);
while (scanf("%lld%lld%lld%lld", &p, &q, &a, &n), p | q | a | n)
{
ans =0;
par[0].a = par[0].b =1;
longlong g = gcd(p, q);
dfs(1, Partition(p / g, q / g), 1);
printf("%lld\n", ans);
}
return0;
}