2015 年 JXNU_ACS 算法组寒假第一次周赛 1001 A+B

A+B

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 37   Accepted Submission(s) : 0
Font: Times New Roman | Verdana | Georgia
Font Size: ← →

Problem Description

整数版的A+B做多了,难免会对其产生厌倦心理,今天我们来尝试下更有趣的分数A+B吧。令A=a/b,B=c/d;求A+B,并以最简分数形式输出结果,最简分数就是分子和分母不能有大于1的公因数的分数。例如给定2/4不是最简分数,因为它们有公因子2,而1/2就是最简分数了,因为它们没有大于1的公因子。

Input

第一行有一个正整数t,表示有t组测试数据。
每组测试数据有四个正整数,a,b,c,d,它们分别用空格隔开。这四个正整数每个的取值范围都是[1,10^50]。

Output

以最简分数的形式输出A+B的结果,每个结果占一行。

Sample Input

3
1 1 1 1
1 2 1 3
2 4 3 6

Sample Output

2/1
5/6
1/1

Author

JXNU_WY 



这道题是本人YY出来的,因为想用一道题来考察大家是否对高精度加减乘除首先,我说一句,打网络赛,模板很重要!
和高精度GCD是否掌握。杭电上有一道题跟此题类似,解题思路是一样的。只不过本题全程都要用高精度。
首先可以知道 a/b+c/d=(a*d+b*c)/(b*d)
令fz=a*d+b*c,fm=b*d
再令g=gcd(fz,fm)
那么由于结果需要最简的分数,所以分子分母应该同除g,所得即为最后结果。
本场是网络赛,可直接套模板。在本场比赛敲一遍模板显然时间是不够的。
也可以用JAVA搞,非常简单。
本人高精度模板全是自己琢磨出来的,比较挫,高精度加减法还好,高精度乘法的复杂度是O(n*n)的,其实高精度乘法可以用快速傅里叶变换优化一下,可以优化成O(n*log(n)),杭电上有一道就是必须用O(n*log(n))复杂度才能过的。然后就是高精度除法,高精除一直是非常让人恶心的,有些人一看到就会感到一阵恶心。我钻研高精除,前前后后也花了非常多的时间。没现实它总觉得很难,实现了它也感觉不过如此。不过跟JAVA高精度库函数相比,我实现的高精除还很弱。
很显然,高精度除法是用减法实现的,但是直接一次次减的话,那就太慢了,所以就要不断试商以加快速度。具体思想可参考《程序设计导引及在线实践》这本书。
最后,我再说一句,打网络赛,模板真的很重要!


下面给出C++的实现代码。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int L=110;
string add(string a,string b)
{
    string ans;
    int na[L]={0},nb[L]={0};
    int la=a.size(),lb=b.size();
    for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';
    for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';
    int lmax=la>lb?la:lb;
    for(int i=0;i<lmax;i++) na[i]+=nb[i],na[i+1]+=na[i]/10,na[i]%=10;
    if(na[lmax]) lmax++;
    for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';
    return ans;
}
string mul(string a,string b)
{
    string s;
    int na[L],nb[L],nc[L],La=a.size(),Lb=b.size();//na存储被乘数,nb存储乘数,nc存储积
    fill(na,na+L,0);fill(nb,nb+L,0);fill(nc,nc+L,0);//将na,nb,nc都置为0
    for(int i=La-1;i>=0;i--) na[La-i]=a[i]-'0';//将字符串表示的大整形数转成i整形数组表示的大整形数
    for(int i=Lb-1;i>=0;i--) nb[Lb-i]=b[i]-'0';
    for(int i=1;i<=La;i++)
        for(int j=1;j<=Lb;j++)
        nc[i+j-1]+=na[i]*nb[j];//a的第i位乘以b的第j位为积的第i+j-1位(先不考虑进位)
    for(int i=1;i<=La+Lb;i++)
        nc[i+1]+=nc[i]/10,nc[i]%=10;//统一处理进位
    if(nc[La+Lb]) s+=nc[La+Lb]+'0';//判断第i+j位上的数字是不是0
    for(int i=La+Lb-1;i>=1;i--)
        s+=nc[i]+'0';//将整形数组转成字符串
    return s;
}
int sub(int *a,int *b,int La,int Lb)
{
    if(La<Lb) return -1;//如果a小于b,则返回-1
    if(La==Lb)
    {
        for(int i=La-1;i>=0;i--)
            if(a[i]>b[i]) break;
            else if(a[i]<b[i]) return -1;//如果a小于b,则返回-1

    }
    for(int i=0;i<La;i++)//高精度减法
    {
        a[i]-=b[i];
        if(a[i]<0) a[i]+=10,a[i+1]--;
    }
    for(int i=La-1;i>=0;i--)
        if(a[i]) return i+1;//返回差的位数
    return 0;//返回差的位数

}
string div(string n1,string n2,int nn)//n1,n2是字符串表示的被除数,除数,nn是选择返回商还是余数
{
    string s,v;//s存商,v存余数
     int a[L],b[L],r[L],La=n1.size(),Lb=n2.size(),i,tp=La;//a,b是整形数组表示被除数,除数,tp保存被除数的长度
     fill(a,a+L,0);fill(b,b+L,0);fill(r,r+L,0);//数组元素都置为0
     for(i=La-1;i>=0;i--) a[La-1-i]=n1[i]-'0';
     for(i=Lb-1;i>=0;i--) b[Lb-1-i]=n2[i]-'0';
     if(La<Lb || (La==Lb && n1<n2)) {
            //cout<<0<<endl;
     return n1;}//如果a<b,则商为0,余数为被除数
     int t=La-Lb;//除被数和除数的位数之差
     for(int i=La-1;i>=0;i--)//将除数扩大10^t倍
        if(i>=t) b[i]=b[i-t];
        else b[i]=0;
     Lb=La;
     for(int j=0;j<=t;j++)
     {
         int temp;
         while((temp=sub(a,b+j,La,Lb-j))>=0)//如果被除数比除数大继续减
         {
             La=temp;
             r[t-j]++;
         }
     }
     for(i=0;i<L-10;i++) r[i+1]+=r[i]/10,r[i]%=10;//统一处理进位
     while(!r[i]) i--;//将整形数组表示的商转化成字符串表示的
     while(i>=0) s+=r[i--]+'0';
     //cout<<s<<endl;
     i=tp;
     while(!a[i]) i--;//将整形数组表示的余数转化成字符串表示的</span>
     while(i>=0) v+=a[i--]+'0';
     if(v.empty()) v="0";
     //cout<<v<<endl;
     if(nn==1) return s;
     if(nn==2) return v;
}
bool judge(string s)//判断s是否为全0串
{
    for(int i=0;i<s.size();i++)
        if(s[i]!='0') return false;
    return true;
}
string gcd(string a,string b)//求最大公约数
{
    string t;
    while(!judge(b))//如果余数不为0,继续除
    {
        t=a;//保存被除数的值
        a=b;//用除数替换被除数
        b=div(t,b,2);//用余数替换除数
    }
    return a;
}
int main()
{
    cin.sync_with_stdio(false);
    string a,b,c,d;
    int t;
    cin>>t;
    while(t--)
    {
        cin>>a>>b>>c>>d;
        string fz=add(mul(a,d),mul(b,c));
        string fm=mul(b,d);
        string g=gcd(fz,fm);
        fz=div(fz,g,1);
        fm=div(fm,g,1);
        cout<<fz<<"/"<<fm<<endl;
    }
    return 0;
}


下面给出JAVA的实现代码。

import java.util.*;
import java.io.*;
import java.math.*;
public class Main {
	static String str;
	public static void main(String[] args) {
		Scanner cin = new Scanner(new BufferedInputStream(System.in));
		int t;
		t=cin.nextInt();
		BigInteger a,b,c,d;
		while(t>0)
		{
			t--;
			a=cin.nextBigInteger();b=cin.nextBigInteger();
			c=cin.nextBigInteger();d=cin.nextBigInteger();
			BigInteger fz=a.multiply(d).add(b.multiply(c));
			BigInteger fm=b.multiply(d);
			BigInteger g=fz.gcd(fm);
			fz=fz.divide(g);fm=fm.divide(g);
			System.out.println(fz+"/"+fm);
		}

	}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值