USACO Fractions to Decimals

1、考察长除法,就是一个很麻烦的模拟。。。注意数组要开足够大,我刚开始开10000都不行,后来开到1000000就过了。。。

2、注意末尾的回车,USACO卡得真严!第二章终于做完了,接下来是更大的挑战。。。

/*
ID:mrxy564
PROG:fracdec
LANG:C++
*/
#include<cstdio>
#include<cstring>
using namespace std;
char s[1000000];
int r[1000000],vis[1000000];
int main(){
 freopen("fracdec.in","r",stdin);
 freopen("fracdec.out","w",stdout);
 int a,b,cnt=0;
    scanf("%d%d",&a,&b);
 int num=a/b;
 sprintf(s,"%d",num);
 cnt=strlen(s);
 s[cnt++]='.';
 a=a%b;
 r[cnt]=a;
    memset(vis,0,sizeof(vis));
 while(!vis[a]&&a!=0){
  s[cnt]=a*10/b+'0';
  cnt++;
  vis[a]=1;
  a=a*10%b;
  r[cnt]=a;
 }
 if(vis[a]){
   for(int i=cnt-1;i>=0;i--)
  if(r[i]==a){
      for(int j=cnt-1;j>=i;j--)
    s[j+1]=s[j];
   s[i]='(';
  }
     cnt++;
        s[cnt++]=')';
  s[cnt++]='\0';
 }
 int len=strlen(s);
 if(s[len-1]=='.') s[len++]='0';
 for(int i=0;i<len;i++){
    printf("%c",s[i]);
    if((i+1)%76==0) printf("\n");
 }
 printf("\n");
 return 0;
}

官方题解:

Remember long division? We know that the decimal expansion is repeating when, after the decimal point, we see a remainder we've seen before. The repeating part will be all the digits we've calculated since the last time we saw that remainder.

We read in the input and print the integer part. Then we do long division on the fractional part until we see a remainder more than once or the remainder becomes zero. If we see a remainder more than once, we're repeating, in which case we print the non-repeated and repeated part appropriately. If the remainder becomes zero, we finished, in which case we print the decimal expansion. When no digits of the decimal expansion have been generated, the correct answer seems to be to print a zero.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define MAXDIGIT 100100

char dec[MAXDIGIT];
int lastrem[MAXDIGIT];
char buf[MAXDIGIT];

void
main(void)
{
    FILE *fin, *fout;
    int n, d, k, i, rem, len;

    fin = fopen("fracdec.in", "r");
    fout = fopen("fracdec.out", "w");
    assert(fin != NULL && fout != NULL);

    fscanf(fin, "%d %d", &n, &d);
    sprintf(buf, "%d.", n/d);

	/* long division keeping track of if we've seem a remainder before */
    for(i=0; i<MAXDIGIT; i++)
	lastrem[i] = -1;

    rem = n % d;
    for(i=0;; i++) {
	if(rem == 0) {
	    if(i == 0)
		sprintf(buf+strlen(buf), "0");
	    else
		sprintf(buf+strlen(buf), "%s", dec);
	    break;
	}
	if(lastrem[rem] != -1) {
	    k = lastrem[rem];
	    sprintf(buf+strlen(buf), "%.*s(%s)", k, dec, dec+k);
	    break;
	}

	lastrem[rem] = i;
	n = rem * 10;
	dec[i] = n/d + '0';
	rem = n%d;
    }

    /* print buf 76 chars per line */
    len = strlen(buf);
    for(i=0; i<len; i+=76) {
    	fprintf(fout, "%.76s\n", buf+i);
    }
    exit(0);
}

Here's a another, more elegant solution from Anatoly Preygel.

Compute the number of digits before the repeat starts, and then you don't even have to store the digits or remainders, making the program use much less memory and go faster. We know that powers of 2 and 5 are the only numbers which do not result in a repeat, so to find the number of digits before the repeat, we just find the maximum of the differences between the powers of 2 and 5 in the denominator and numerator (see code snippet). Then we just use the first remainder, and output each digit as we calculate it:

#include <iostream.h>
#include <fstream.h>
#include <math.h>
ofstream out("fracdec.out");

int colcount=0;

int numBeforeRepeat(int n, int d) {
    int c2=0, c5=0;
    if (n == 0) return 1;
    while (d%2==0) { d/=2; c2++; }
    while (d%5==0) { d/=5; c5++; }
    while (n%2==0) { n/=2; c2--; } /* can go negative */
    while (n%5==0) { n/=5; c5--; } /* can go negative */
    if (c2>c5)
        if (c2>0) return c2;
        else return 0;
    else
        if (c5>0) return c5;
        else return 0;
}

void print (char c) {
    if (colcount==76) {
        out<<endl;
        colcount=0;
    }
    out<<c;
    colcount++;
}

void print (int n) {
    if (n>=10) print (n/10);
    print ((char)('0'+(n%10)));
}

int main() {
    int n, d;
    ifstream in("fracdec.in");
    in>>n>>d;
    in.close();

    print (n/d);
    print ('.');
    n=n%d;
    int m=numBeforeRepeat(n,d);
    for(int i=0;i<m;i++) {
        n*=10;
	print (n/d);
        n%=d;
    }
    int r=n;
    if(r!=0) {
	print ('(');
        do {
            n*=10;
	    print (n/d);
            n%=d;
        } while (n!=r);
	print (')');
    }
    out<<endl;
    out.close();
    exit (0);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值