zoukankan      html  css  js  c++  java
  • POJ 1980【Unit Fraction Partition】

    描述

      给出数字P,Q,A,N,代表将分数P/Q分解成至多N个分数之和,这些分数的分子全为1,且分母的乘积不超过A。例如当输入数据为2 3 120 3时,我们可以得到以下几种分法:

    输入输出格式

    输入

      本题含有多组测试数据,每组给出四个数P,Q,A,N,其中 p,q <= 800, A <= 12000,N <= 7.当输入的四个数均为0时,代表测试结束.

    输出

      针对每组数据,输出共有多少种不同的分法。

    输入输出样例

    输入样例1

    2 3 120 3
    2 3 300 3
    2 3 299 3
    2 3 12 3
    2 3 12000 7
    54 795 12000 7
    2 3 300 1
    2 1 200 5
    2 4 54 2
    0 0 0 0
    

    输出样例1

    4
    7
    6
    2
    42
    1
    0
    9
    3
    

    解题思路

      这道题花了我两个小时。刚开始我用了最大公约数来求最小公倍数什么的,代码很麻烦,要同时约分什么的,但是其实只要几个简单易懂剪枝(只要你有足够的数学基本功)就行了,最后输出。

    题解

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int ans,p,q,a,n;
     4 void dfs(int dep,int pre,int fz,int fm,int sum)//深度,上一个的下标,当前分子,当前分母,当前乘积 
     5 {
     6     if(sum>a)return;//乘积超过就返回 
     7     if(fz*q==fm*p)//直接最暴力的分母乘分母通分 
     8     {
     9         ans++;//方案数加加 
    10         return;
    11     }
    12     if(fz*q>fm*p||dep==n+1)return;//这个也靠自己理解,是指当前分数比目标分数大,还有分数数量超过了n 
    13     for(int i=pre;i<=a/sum;i++)//循环 
    14     {
    15         dfs(dep+1,i,fz*i+fm,fm*i,sum*i);//愉快的搜索 
    16     }
    17 }
    18 int main()
    19 {
    20     while(cin>>p>>q>>a>>n)//注意格式 
    21     {
    22         if(q==0)return 0;//反正分母不为零 
    23         ans=0;//注意清空 
    24         dfs(1,1,0,1,1);//搜索 
    25         cout<<ans<<endl;//输出 
    26     }
    27     return 0;
    28 }

     然而大家看一看这个程序的运行时间

    Time:364

    然后我在下次考试中优化了一下

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int p,q,a,n,qwe,ans;
     4 void dfs(int fz,int fm,int dep,int pre,int sum)
     5 {
     6     if(dep==qwe+1)
     7     {
     8         if(fz==0)
     9         {
    10             ans++;
    11         }
    12         return;
    13     }
    14     for(int i=pre;i<=a/sum;i++)
    15     {
    16         if(fz*i<fm)continue;
    17         if(i>fm*(qwe+1-dep)/fz)break;//优化
    18         dfs(fz*i-fm,fm*i,dep+1,i,sum*i);
    19     }
    20 }
    21 int main()
    22 {
    23     while(cin>>p>>q>>a>>n&&q)
    24     {
    25         ans=0;
    26         for(qwe=1;qwe<=n;qwe++)
    27             dfs(p,q,1,1,1);
    28         cout<<ans<<endl;
    29     }
    30 }

    Time:56

      再来看看另一种方法

    解题思路

      传入的参数与之前有所不同,这次的分子和分母是目标分数减去以枚举的分数剩下的分数值,再把分母枚举的范围剪枝一波,时间就大大减少了。

      再看到13行:

          我们先看枚举的起点,这里拿个两个参数作比较,pre是上一个分母的值,fm/fz是我们把这一个分数假装是最后一个,能取到的最大值,就是防止最后减出的值为负数,两者取大值。

          再看枚举的重点,这里依旧有两个参数作比较,前者是防止乘积超出,后者是一种极端情况,把剩余分数值平均分给剩下所有的分数,即保证剩下的分数之和大于等于当前剩余分数值。

    题解

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int p,q,a,n,ans;
     4 void dfs(int fz,int fm,int sum,int dep,int pre) //剩余分子分母,乘积,深度,上一个分母 
     5 {
     6     if(sum>a)return;//乘积超过 
     7     if(fz==0)//分子为零 
     8     {
     9         ans++;//方案数加加 
    10         return;
    11     }
    12     if(dep==n+1)return;//分数数量超过 
    13     for(int i=max(pre,fm/fz);i<=min(a/sum,(n+1-dep)*fm/fz);i++)//请见解题思路 
    14     {
    15         dfs(fz*i-fm,fm*i,sum*i,dep+1,i);//搜索 
    16     }
    17 }
    18 int main()
    19 {
    20     while(cin>>p>>q>>a>>n)//以下都不需要注释了 
    21     {
    22         if(q==0)return 0;
    23         ans=0;
    24         dfs(p,q,1,1,1); 
    25         cout<<ans<<endl;
    26     }
    27 }

    我们再看看这一次代码的运行时间

    Time:36

    说实话,都没想到这么快(笑哭)。

  • 相关阅读:
    新加的keyword编码错误
    Robot framework模拟打开浏览器问题
    Chrome无法登陆
    Android App用MulticastSocket监听组播,为什么连接到不同路由、在不同手机上跑,有的能收到有的收不到
    ubuntu 14.04/15.10 安装基于eclipse的android app开发环境
    ubuntu15.10英文系统中文输入法配置 fcitx
    Amazon S3 上传文件 SSL23_GET_SERVER_HELLO握手错误
    Google Map API v2 番外篇 关于gps位置偏差及修正方法探讨
    Google Map API v2 (四)----- 导航路径
    Google Map API v2 (三)----- 地图上添加标记(Marker),标记info窗口,即指定经纬度获取地址字符串
  • 原文地址:https://www.cnblogs.com/hualian/p/11157516.html
Copyright © 2011-2022 走看看